Merge "Fix OHM DA Organizer do not update display bounds by orientation" into sc-dev
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 29bcfe0..1bd90a8 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPriv.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index be172e6..544bca02 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShim.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 13eca13..72386bb 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPriv.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 2e863fe..893eac2 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShim.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/Android.bp b/Android.bp
index c5980b9..9690969 100644
--- a/Android.bp
+++ b/Android.bp
@@ -584,7 +584,7 @@
         "android.hardware.vibrator-V2-java",
         "android.security.apc-java",
         "android.security.authorization-java",
-        "android.security.usermanager-java",
+        "android.security.maintenance-java",
         "android.security.vpnprofilestore-java",
         "android.system.keystore2-V1-java",
         "android.system.suspend.control.internal-java",
@@ -665,9 +665,8 @@
     ],
     required: [
         "framework-platform-compat-config",
-        // TODO: remove gps_debug, cec_config.xml and protolog.conf.json when the build system propagates "required" properly.
+        // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
         "gps_debug.conf",
-        "cec_config.xml",
         "icu4j-platform-compat-config",
         "libcore-platform-compat-config",
         "protolog.conf.json.gz",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d08c527..9ceef6b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -82,5 +82,65 @@
     {
       "name": "ManagedProfileLifecycleStressTest"
     }
-  ]
+  ],
+ "auto-postsubmit": [
+   // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+   // automotive targets to avoid introducing additional test flake and build time. The plan for
+   // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+   // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+   // infra during the hardening phase.
+   // TODO: this tag to be removed once the above is no longer an issue.
+   {
+     "name": "FrameworksUiServicesTests",
+     "options": [
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       }
+     ]
+   },
+   {
+     "name": "ExtServicesUnitTests",
+     "options": [
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       }
+     ]
+   },
+   {
+     "name": "TestablesTests",
+     "options": [
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       }
+     ]
+   },
+   {
+     "name": "FrameworksCoreTests",
+     "options": [
+       {
+         "include-annotation": "android.platform.test.annotations.Presubmit"
+       },
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       },
+       {
+         "exclude-annotation": "org.junit.Ignore"
+       }
+     ]
+   },
+   {
+     "name": "FrameworksServicesTests",
+     "options": [
+       {
+         "include-annotation": "android.platform.test.annotations.Presubmit"
+       },
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       },
+       {
+         "exclude-annotation": "org.junit.Ignore"
+       }
+     ]
+   }
+ ]
 }
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 5ded446..f6bfaa3 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -18,9 +18,10 @@
   }
 
   public static final class AppSearchManager.SearchContext.Builder {
-    ctor public AppSearchManager.SearchContext.Builder();
+    ctor @Deprecated public AppSearchManager.SearchContext.Builder();
+    ctor public AppSearchManager.SearchContext.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build();
-    method @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
+    method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
   }
 
   public interface AppSearchMigrationHelper {
@@ -115,7 +116,6 @@
 
   public abstract static class AppSearchSchema.PropertyConfig {
     method public int getCardinality();
-    method public int getDataType();
     method @NonNull public String getName();
     field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
     field public static final int CARDINALITY_REPEATED = 1; // 0x1
@@ -180,14 +180,15 @@
     method public int getScore();
     method public long getTtlMillis();
     method @NonNull public String getUri();
-    field public static final String DEFAULT_NAMESPACE = "";
+    field @Deprecated public static final String DEFAULT_NAMESPACE = "";
   }
 
   public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
-    ctor public GenericDocument.Builder(@NonNull String, @NonNull String);
+    ctor @Deprecated public GenericDocument.Builder(@NonNull String, @NonNull String);
+    ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String);
     method @NonNull public android.app.appsearch.GenericDocument build();
     method @NonNull public BuilderType setCreationTimestampMillis(long);
-    method @NonNull public BuilderType setNamespace(@NonNull String);
+    method @Deprecated @NonNull public BuilderType setNamespace(@NonNull String);
     method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
     method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
     method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
@@ -206,12 +207,13 @@
   }
 
   public static final class GetByUriRequest.Builder {
-    ctor public GetByUriRequest.Builder();
+    ctor @Deprecated public GetByUriRequest.Builder();
+    ctor public GetByUriRequest.Builder(@NonNull 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>);
     method @NonNull public android.app.appsearch.GetByUriRequest build();
-    method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
+    method @Deprecated @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
   }
 
   public class GlobalSearchSession implements java.io.Closeable {
@@ -242,11 +244,12 @@
   }
 
   public static final class RemoveByUriRequest.Builder {
-    ctor public RemoveByUriRequest.Builder();
+    ctor @Deprecated public RemoveByUriRequest.Builder();
+    ctor public RemoveByUriRequest.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.RemoveByUriRequest build();
-    method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
+    method @Deprecated @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
   }
 
   public final class ReportUsageRequest {
@@ -256,30 +259,50 @@
   }
 
   public static final class ReportUsageRequest.Builder {
-    ctor public ReportUsageRequest.Builder();
+    ctor @Deprecated public ReportUsageRequest.Builder();
+    ctor public ReportUsageRequest.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.ReportUsageRequest build();
-    method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
+    method @Deprecated @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
     method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String);
     method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long);
   }
 
   public final class SearchResult {
     method @NonNull public String getDatabaseName();
-    method @NonNull public android.app.appsearch.GenericDocument getDocument();
+    method @Deprecated @NonNull public android.app.appsearch.GenericDocument getDocument();
+    method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
     method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
     method @NonNull public String getPackageName();
   }
 
+  public static final class SearchResult.Builder {
+    ctor public SearchResult.Builder(@NonNull String, @NonNull String);
+    method @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo);
+    method @NonNull public android.app.appsearch.SearchResult build();
+    method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument);
+  }
+
   public static final class SearchResult.MatchInfo {
     method @NonNull public CharSequence getExactMatch();
-    method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+    method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
+    method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange();
     method @NonNull public String getFullText();
     method @NonNull public String getPropertyPath();
     method @NonNull public CharSequence getSnippet();
-    method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+    method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
+    method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange();
+  }
+
+  public static final class SearchResult.MatchInfo.Builder {
+    ctor public SearchResult.MatchInfo.Builder();
+    method @NonNull public android.app.appsearch.SearchResult.MatchInfo build();
+    method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
+    method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setPropertyPath(@NonNull String);
+    method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange);
   }
 
   public static final class SearchResult.MatchRange {
+    ctor public SearchResult.MatchRange(int, int);
     method public int getEnd();
     method public int getStart();
   }
@@ -361,6 +384,19 @@
     method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> getMigrationFailures();
   }
 
+  public static final class SetSchemaResponse.Builder {
+    ctor public SetSchemaResponse.Builder();
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedType(@NonNull String);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedTypes(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailure(@NonNull android.app.appsearch.SetSchemaResponse.MigrationFailure);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailures(@NonNull java.util.Collection<android.app.appsearch.SetSchemaResponse.MigrationFailure>);
+    method @NonNull public android.app.appsearch.SetSchemaResponse build();
+  }
+
   public static class SetSchemaResponse.MigrationFailure {
     method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult();
     method @NonNull public String getNamespace();
@@ -368,11 +404,23 @@
     method @NonNull public String getUri();
   }
 
+  public static final class SetSchemaResponse.MigrationFailure.Builder {
+    ctor public SetSchemaResponse.MigrationFailure.Builder();
+    method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure build();
+    method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setAppSearchResult(@NonNull android.app.appsearch.AppSearchResult<java.lang.Void>);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setNamespace(@NonNull String);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setSchemaType(@NonNull String);
+    method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setUri(@NonNull String);
+  }
+
 }
 
 package android.app.appsearch.exceptions {
 
   public class AppSearchException extends java.lang.Exception {
+    ctor public AppSearchException(int);
+    ctor public AppSearchException(int, @Nullable String);
+    ctor public AppSearchException(int, @Nullable String, @Nullable Throwable);
     method public int getResultCode();
     method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult();
   }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index da446bf..0c6b86b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -19,19 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
-import android.os.Bundle;
-import android.os.ParcelableException;
-import android.os.RemoteException;
 
-import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.Preconditions;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -39,17 +30,19 @@
  * Provides access to the centralized AppSearch index maintained by the system.
  *
  * <p>AppSearch is a search library for managing structured data featuring:
+ *
  * <ul>
- *     <li>A fully offline on-device solution
- *     <li>A set of APIs for applications to index documents and retrieve them via full-text search
- *     <li>APIs for applications to allow the System to display their content on system UI surfaces
- *     <li>Similarly, APIs for applications to allow the System to share their content with other
- *     specified applications.
+ *   <li>A fully offline on-device solution
+ *   <li>A set of APIs for applications to index documents and retrieve them via full-text search
+ *   <li>APIs for applications to allow the System to display their content on system UI surfaces
+ *   <li>Similarly, APIs for applications to allow the System to share their content with other
+ *       specified applications.
  * </ul>
  *
  * <p>Applications create a database by opening an {@link AppSearchSession}.
  *
  * <p>Example:
+ *
  * <pre>
  * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
  *
@@ -60,11 +53,12 @@
  * });</pre>
  *
  * <p>After opening the session, a schema must be set in order to define the organizational
- * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema
- * is composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique
- * type of data.
+ * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema is
+ * composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique type
+ * of data.
  *
  * <p>Example:
+ *
  * <pre>
  * AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
  *     .addProperty(new StringPropertyConfig.Builder("subject")
@@ -82,15 +76,16 @@
  * });</pre>
  *
  * <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object,
- * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a
- * logical group of documents. For example, a namespace can be created to group documents on a
- * per-account basis. A URI identifies a single document within a namespace. The combination
- * of URI and namespace uniquely identifies a {@link GenericDocument} in the database.
+ * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a logical
+ * group of documents. For example, a namespace can be created to group documents on a per-account
+ * basis. A URI identifies a single document within a namespace. The combination of URI and
+ * namespace uniquely identifies a {@link GenericDocument} in the database.
  *
- * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database
- * and indexed by calling {@link AppSearchSession#put}.
+ * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database and
+ * indexed by calling {@link AppSearchSession#put}.
  *
  * <p>Example:
+ *
  * <pre>
  * // Although for this example we use GenericDocument directly, we recommend extending
  * // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
@@ -115,18 +110,12 @@
  * and namespace.
  *
  * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove
- * operation. Remove operations can be done by URI and namespace via
- * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)},
- * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
+ * operation. Remove operations can be done by URI and namespace via {@link
+ * AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)}, or by query via
+ * {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
  */
 @SystemService(Context.APP_SEARCH_SERVICE)
 public class AppSearchManager {
-    /**
-     * The default empty database name.
-     *
-     * @hide
-     */
-    public static final String DEFAULT_DATABASE_NAME = "";
 
     private final IAppSearchManager mService;
     private final Context mContext;
@@ -158,10 +147,42 @@
 
         /** Builder for {@link SearchContext} objects. */
         public static final class Builder {
-            private String mDatabaseName = DEFAULT_DATABASE_NAME;
+            private String mDatabaseName;
             private boolean mBuilt = false;
 
             /**
+             * TODO(b/181887768): This method exists only for dogfooder transition and must be
+             * removed.
+             *
+             * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
+             *     method exists only for dogfooder transition and must be removed.
+             */
+            @Deprecated
+            public Builder() {
+                mDatabaseName = "";
+            }
+
+            /**
+             * Creates a new {@link SearchContext.Builder}.
+             *
+             * <p>{@link AppSearchSession} will create or open a database under the given name.
+             *
+             * <p>Databases with different names are fully separate with distinct types, namespaces,
+             * and data.
+             *
+             * <p>Database name cannot contain {@code '/'}.
+             *
+             * @param databaseName The name of the database.
+             * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+             */
+            public Builder(@NonNull String databaseName) {
+                Objects.requireNonNull(databaseName);
+                Preconditions.checkArgument(
+                        !databaseName.contains("/"), "Database name cannot contain '/'");
+                mDatabaseName = databaseName;
+            }
+
+            /**
              * Sets the name of the database associated with {@link AppSearchSession}.
              *
              * <p>{@link AppSearchSession} will create or open a database under the given name.
@@ -173,16 +194,21 @@
              *
              * <p>If not specified, defaults to the empty string.
              *
+             * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+             * removed.
+             *
              * @param databaseName The name of the database.
              * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+             * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
+             *     method exists only for dogfooder transition and must be removed.
              */
+            @Deprecated
             @NonNull
             public Builder setDatabaseName(@NonNull String databaseName) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Objects.requireNonNull(databaseName);
-                if (databaseName.contains("/")) {
-                    throw new IllegalArgumentException("Database name cannot contain '/'");
-                }
+                Preconditions.checkArgument(
+                        !databaseName.contains("/"), "Database name cannot contain '/'");
                 mDatabaseName = databaseName;
                 return this;
             }
@@ -246,349 +272,9 @@
                 mService, mContext.getUserId(), getPackageName(), executor, callback);
     }
 
-    /**
-     * Sets the schema being used by documents provided to the {@link #putDocuments} method.
-     *
-     * <p>The schema provided here is compared to the stored copy of the schema previously supplied
-     * to {@link #setSchema}, if any, to determine how to treat existing documents. The following
-     * types of schema modifications are always safe and are made without deleting any existing
-     * documents:
-     *
-     * <ul>
-     *   <li>Addition of new types
-     *   <li>Addition of new {@link
-     *       android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
-     *       {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED
-     *       REPEATED} properties to a type
-     *   <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link
-     *       android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL}
-     *       property into a {@link
-     *       android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED}
-     *       property.
-     * </ul>
-     *
-     * <p>The following types of schema changes are not backwards-compatible:
-     *
-     * <ul>
-     *   <li>Removal of an existing type
-     *   <li>Removal of a property from a type
-     *   <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
-     *   <li>For properties of {@code GenericDocument} type, changing the schema type of {@code
-     *       GenericDocument}s of that property
-     *   <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link
-     *       android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL}
-     *       property into a {@link
-     *       android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED}
-     *       property).
-     *   <li>Adding a {@link
-     *       android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED}
-     *       property.
-     * </ul>
-     *
-     * <p>Supplying a schema with such changes will result in this call returning an {@link
-     * AppSearchResult} with a code of {@link AppSearchResult#RESULT_INVALID_SCHEMA} and an error
-     * message describing the incompatibility. In this case the previously set schema will remain
-     * active.
-     *
-     * <p>If you need to make non-backwards-compatible changes as described above, instead use the
-     * {@link #setSchema(List, boolean)} method with the {@code forceOverride} parameter set to
-     * {@code true}.
-     *
-     * <p>It is a no-op to set the same schema as has been previously set; this is handled
-     * efficiently.
-     *
-     * @param request The schema update request.
-     * @return the result of performing this operation.
-     * @hide
-     * @deprecated use {@link AppSearchSession#setSchema} instead.
-     */
-    @NonNull
-    public AppSearchResult<Void> setSchema(@NonNull SetSchemaRequest request) {
-        Preconditions.checkNotNull(request);
-        // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to
-        //  avoid binder limits.
-        List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
-        for (AppSearchSchema schema : request.getSchemas()) {
-            schemaBundles.add(schema.getBundle());
-        }
-        AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
-        try {
-            mService.setSchema(
-                    getPackageName(),
-                    DEFAULT_DATABASE_NAME,
-                    schemaBundles,
-                    new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
-                    /*schemasPackageAccessible=*/ Collections.emptyMap(),
-                    request.isForceOverride(),
-                    mContext.getUserId(),
-                    new IAppSearchResultCallback.Stub() {
-                        public void onResult(AppSearchResult result) {
-                            future.complete(result);
-                        }
-                    });
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        return getFutureOrThrow(future);
-    }
-
-    /**
-     * Index {@link GenericDocument}s into AppSearch.
-     *
-     * <p>You should not call this method directly; instead, use the {@code
-     * AppSearch#putDocuments()} API provided by JetPack.
-     *
-     * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
-     * schema type previously registered via the {@link #setSchema} method.
-     *
-     * @param request {@link PutDocumentsRequest} containing documents to be indexed
-     * @return The pending result of performing this operation. The keys of the returned {@link
-     *     AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if
-     *     they were successfully indexed, or a failed {@link AppSearchResult} otherwise.
-     * @throws RuntimeException If an error occurred during the execution.
-     * @hide
-     * @deprecated use {@link AppSearchSession#put} instead.
-     */
-    public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
-        // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
-        // one big list.
-        List<GenericDocument> documents = request.getGenericDocuments();
-        List<Bundle> documentBundles = new ArrayList<>(documents.size());
-        for (int i = 0; i < documents.size(); i++) {
-            documentBundles.add(documents.get(i).getBundle());
-        }
-        AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
-        try {
-            mService.putDocuments(
-                    getPackageName(),
-                    DEFAULT_DATABASE_NAME,
-                    documentBundles,
-                    mContext.getUserId(),
-                    new IAppSearchBatchResultCallback.Stub() {
-                        public void onResult(AppSearchBatchResult result) {
-                            future.complete(result);
-                        }
-
-                        public void onSystemError(ParcelableException exception) {
-                            future.completeExceptionally(exception);
-                        }
-                    });
-        } catch (RemoteException e) {
-            future.completeExceptionally(e);
-        }
-        return getFutureOrThrow(future);
-    }
-
-    /**
-     * Retrieves {@link GenericDocument}s by URI.
-     *
-     * <p>You should not call this method directly; instead, use the {@code
-     * AppSearch#getDocuments()} API provided by JetPack.
-     *
-     * @param request {@link GetByUriRequest} containing URIs to be retrieved.
-     * @return The pending result of performing this operation. The keys of the returned {@link
-     *     AppSearchBatchResult} are the input URIs. The values are the returned {@link
-     *     GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise. URIs that
-     *     are not found will return a failed {@link AppSearchResult} with a result code of {@link
-     *     AppSearchResult#RESULT_NOT_FOUND}.
-     * @throws RuntimeException If an error occurred during the execution.
-     * @hide
-     * @deprecated use {@link AppSearchSession#getByUri} instead.
-     */
-    public AppSearchBatchResult<String, GenericDocument> getByUri(
-            @NonNull GetByUriRequest request) {
-        // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
-        //     them in one big list.
-        List<String> uris = new ArrayList<>(request.getUris());
-        AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
-        try {
-            mService.getDocuments(
-                    getPackageName(),
-                    DEFAULT_DATABASE_NAME,
-                    request.getNamespace(),
-                    uris,
-                    request.getProjectionsInternal(),
-                    mContext.getUserId(),
-                    new IAppSearchBatchResultCallback.Stub() {
-                        public void onResult(AppSearchBatchResult result) {
-                            future.complete(result);
-                        }
-
-                        public void onSystemError(ParcelableException exception) {
-                            future.completeExceptionally(exception);
-                        }
-                    });
-        } catch (RemoteException e) {
-            future.completeExceptionally(e);
-        }
-
-        // Translate from document bundles to GenericDocument instances
-        AppSearchBatchResult<String, Bundle> bundleResult = getFutureOrThrow(future);
-        AppSearchBatchResult.Builder<String, GenericDocument> documentResultBuilder =
-                new AppSearchBatchResult.Builder<>();
-
-        // Translate successful results
-        for (Map.Entry<String, Bundle> bundleEntry : bundleResult.getSuccesses().entrySet()) {
-            GenericDocument document;
-            try {
-                document = new GenericDocument(bundleEntry.getValue());
-            } catch (Throwable t) {
-                // These documents went through validation, so how could this fail? We must have
-                // done something wrong.
-                documentResultBuilder.setFailure(
-                        bundleEntry.getKey(),
-                        AppSearchResult.RESULT_INTERNAL_ERROR,
-                        t.getMessage());
-                continue;
-            }
-            documentResultBuilder.setSuccess(bundleEntry.getKey(), document);
-        }
-
-        // Translate failed results
-        for (Map.Entry<String, AppSearchResult<Bundle>> bundleEntry :
-                bundleResult.getFailures().entrySet()) {
-            documentResultBuilder.setFailure(
-                    bundleEntry.getKey(),
-                    bundleEntry.getValue().getResultCode(),
-                    bundleEntry.getValue().getErrorMessage());
-        }
-
-        return documentResultBuilder.build();
-    }
-
-    /**
-     * Searches a document based on a given query string.
-     *
-     * <p>You should not call this method directly; instead, use the {@code AppSearch#query()} API
-     * provided by JetPack.
-     *
-     * <p>Currently we support following features in the raw query format:
-     *
-     * <ul>
-     *   <li>AND
-     *       <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and ‘cat’”).
-     *       Example: hello world matches documents that have both ‘hello’ and ‘world’
-     *   <li>OR
-     *       <p>OR joins (e.g. “match documents that have either the term ‘dog’ or ‘cat’”). Example:
-     *       dog OR puppy
-     *   <li>Exclusion
-     *       <p>Exclude a term (e.g. “match documents that do not have the term ‘dog’”). Example:
-     *       -dog excludes the term ‘dog’
-     *   <li>Grouping terms
-     *       <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
-     *       “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
-     *       Example: (dog puppy) (cat kitten) two one group containing two terms.
-     *   <li>Property restricts
-     *       <p>Specifies which properties of a document to specifically match terms in (e.g. “match
-     *       documents where the ‘subject’ property contains ‘important’”). Example:
-     *       subject:important matches documents with the term ‘important’ in the ‘subject’ property
-     *   <li>Schema type restricts
-     *       <p>This is similar to property restricts, but allows for restricts on top-level
-     *       document fields, such as schema_type. Clients should be able to limit their query to
-     *       documents of a certain schema_type (e.g. “match documents that are of the ‘Email’
-     *       schema_type”). Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will
-     *       match documents that contain the query term ‘dog’ and are of either the ‘Email’ schema
-     *       type or the ‘Video’ schema type.
-     * </ul>
-     *
-     * @param queryExpression Query String to search.
-     * @param searchSpec Spec for setting filters, raw query etc.
-     * @throws RuntimeException If an error occurred during the execution.
-     * @hide
-     * @deprecated use AppSearchSession#query instead.
-     */
-    @NonNull
-    public AppSearchResult<List<SearchResult>> query(
-            @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
-        // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
-        //     them in one big list.
-        AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
-        try {
-            mService.query(
-                    getPackageName(),
-                    DEFAULT_DATABASE_NAME,
-                    queryExpression,
-                    searchSpec.getBundle(),
-                    mContext.getUserId(),
-                    new IAppSearchResultCallback.Stub() {
-                        public void onResult(AppSearchResult result) {
-                            future.complete(result);
-                        }
-                    });
-            AppSearchResult<Bundle> bundleResult = getFutureOrThrow(future);
-            if (!bundleResult.isSuccess()) {
-                return AppSearchResult.newFailedResult(
-                        bundleResult.getResultCode(), bundleResult.getErrorMessage());
-            }
-            SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue());
-            return AppSearchResult.newSuccessfulResult(searchResultPage.getResults());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (Throwable t) {
-            return AppSearchResult.throwableToFailedResult(t);
-        }
-    }
-
-    /**
-     * Removes {@link GenericDocument}s by URI.
-     *
-     * <p>You should not call this method directly; instead, use the {@code AppSearch#delete()} API
-     * provided by JetPack.
-     *
-     * @param request Request containing URIs to be removed.
-     * @return The pending result of performing this operation. The keys of the returned {@link
-     *     AppSearchBatchResult} are the input URIs. The values are {@code null} on success, or a
-     *     failed {@link AppSearchResult} otherwise. URIs that are not found will return a failed
-     *     {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
-     * @throws RuntimeException If an error occurred during the execution.
-     * @hide
-     * @deprecated use {@link AppSearchSession#remove} instead.
-     */
-    public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
-        List<String> uris = new ArrayList<>(request.getUris());
-        AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
-        try {
-            mService.removeByUri(
-                    getPackageName(),
-                    DEFAULT_DATABASE_NAME,
-                    request.getNamespace(),
-                    uris,
-                    mContext.getUserId(),
-                    new IAppSearchBatchResultCallback.Stub() {
-                        public void onResult(AppSearchBatchResult result) {
-                            future.complete(result);
-                        }
-
-                        public void onSystemError(ParcelableException exception) {
-                            future.completeExceptionally(exception);
-                        }
-                    });
-        } catch (RemoteException e) {
-            future.completeExceptionally(e);
-        }
-        return getFutureOrThrow(future);
-    }
-
     /** Returns the package name that should be used for uid verification. */
     @NonNull
     private String getPackageName() {
         return mContext.getOpPackageName();
     }
-
-    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/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index f379739..486acb4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -241,6 +241,7 @@
             documentBundles.add(documents.get(i).getBundle());
         }
         try {
+            // TODO(b/173532925) a timestamp needs to be sent here to calculate binder latency
             mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
index d394904..77740f8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
@@ -152,14 +152,14 @@
 
     /** The builder class for {@link AppSearchEmail}. */
     public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
-
         /**
          * Creates a new {@link AppSearchEmail.Builder}
          *
+         * @param namespace The namespace of the Email.
          * @param uri The Uri of the Email.
          */
-        public Builder(@NonNull String uri) {
-            super(uri, SCHEMA_TYPE);
+        public Builder(@NonNull String namespace, @NonNull String uri) {
+            super(namespace, uri, SCHEMA_TYPE);
         }
 
         /** Sets the from address of {@link AppSearchEmail} */
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 8bf438d..2cf5271 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -309,7 +309,11 @@
             return mBundle.getString(NAME_FIELD, "");
         }
 
-        /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
+        /**
+         * Returns the type of data the property contains (e.g. string, int, bytes, etc).
+         *
+         * @hide
+         */
         public @DataType int getDataType() {
             return mBundle.getInt(DATA_TYPE_FIELD, -1);
         }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 72bb9f3..4ce95ea 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -46,8 +46,14 @@
 public class GenericDocument {
     private static final String TAG = "AppSearchGenericDocumen";
 
-    /** The default empty namespace. */
-    public static final String DEFAULT_NAMESPACE = "";
+    /**
+     * The default empty namespace.
+     *
+     * <p>TODO(b/181887768): This exists only for dogfooder transition and must be removed.
+     *
+     * @deprecated This exists only for dogfooder transition and must be removed.
+     */
+    @Deprecated public static final String DEFAULT_NAMESPACE = "";
 
     /** The maximum number of elements in a repeatable field. */
     private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
@@ -141,7 +147,7 @@
     /** Returns the namespace of the {@link GenericDocument}. */
     @NonNull
     public String getNamespace() {
-        return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+        return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ "");
     }
 
     /** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */
@@ -579,6 +585,9 @@
          *
          * <p>Once {@link #build} is called, the instance can no longer be used.
          *
+         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+         * removed.
+         *
          * @param uri the URI to set for the {@link GenericDocument}.
          * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
          *     provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
@@ -586,7 +595,10 @@
          *     using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
          *     {@link AppSearchSession#put} with result code {@link
          *     AppSearchResult#RESULT_NOT_FOUND}.
+         * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
+         *     instead. This method exists only for dogfooder transition and must be removed.
          */
+        @Deprecated
         @SuppressWarnings("unchecked")
         public Builder(@NonNull String uri, @NonNull String schemaType) {
             Preconditions.checkNotNull(uri);
@@ -604,6 +616,41 @@
         }
 
         /**
+         * Creates a new {@link GenericDocument.Builder}.
+         *
+         * <p>Once {@link #build} is called, the instance can no longer be used.
+         *
+         * <p>URIs are unique within a namespace.
+         *
+         * <p>The number of namespaces per app should be kept small for efficiency reasons.
+         *
+         * @param namespace the namespace to set for the {@link GenericDocument}.
+         * @param uri the URI to set for the {@link GenericDocument}.
+         * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
+         *     provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
+         *     prior to inserting a document of this {@code schemaType} into the AppSearch index
+         *     using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
+         *     {@link AppSearchSession#put} with result code {@link
+         *     AppSearchResult#RESULT_NOT_FOUND}.
+         */
+        @SuppressWarnings("unchecked")
+        public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) {
+            Preconditions.checkNotNull(namespace);
+            Preconditions.checkNotNull(uri);
+            Preconditions.checkNotNull(schemaType);
+            mBuilderTypeInstance = (BuilderType) this;
+            mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
+            mBundle.putString(GenericDocument.URI_FIELD, uri);
+            mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
+            // Set current timestamp for creation timestamp by default.
+            mBundle.putLong(
+                    GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
+            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+            mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
+            mBundle.putBundle(PROPERTIES_FIELD, mProperties);
+        }
+
+        /**
          * Sets the app-defined namespace this document resides in. No special values are reserved
          * or understood by the infrastructure.
          *
@@ -611,8 +658,14 @@
          *
          * <p>The number of namespaces per app should be kept small for efficiency reasons.
          *
+         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+         * removed.
+         *
          * @throws IllegalStateException if the builder has already been used.
+         * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
+         *     instead. This method exists only for dogfooder transition and must be removed.
          */
+        @Deprecated
         @NonNull
         public BuilderType setNamespace(@NonNull String namespace) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
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 17266f8..6881a27 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -107,19 +107,40 @@
      * <p>Once {@link #build} is called, the instance can no longer be used.
      */
     public static final class Builder {
-        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private String mNamespace;
         private final Set<String> mUris = new ArraySet<>();
         private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
         private boolean mBuilt = false;
 
         /**
+         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+         *
+         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+         *     exists only for dogfooder transition and must be removed.
+         */
+        @Deprecated
+        public Builder() {
+            mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        }
+
+        /** Creates a {@link GetByUriRequest.Builder} instance. */
+        public Builder(@NonNull String namespace) {
+            mNamespace = Preconditions.checkNotNull(namespace);
+        }
+
+        /**
          * Sets the namespace to retrieve documents for.
          *
-         * <p>If this is not called, the namespace defaults to {@link
-         * GenericDocument#DEFAULT_NAMESPACE}.
+         * <p>If this is not called, the namespace defaults to an empty string.
+         *
+         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+         * removed.
          *
          * @throws IllegalStateException if the builder has already been used.
+         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+         *     exists only for dogfooder transition and must
          */
+        @Deprecated
         @NonNull
         public Builder setNamespace(@NonNull String namespace) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 39b53b6..455cf3a 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -59,17 +59,39 @@
      * <p>Once {@link #build} is called, the instance can no longer be used.
      */
     public static final class Builder {
-        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private String mNamespace;
         private final Set<String> mUris = new ArraySet<>();
         private boolean mBuilt = false;
 
         /**
+         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+         *
+         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+         *     exists only for dogfooder transition and must be removed.
+         */
+        @Deprecated
+        public Builder() {
+            mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        }
+
+        /** Creates a {@link RemoveByUriRequest.Builder} instance. */
+        public Builder(@NonNull String namespace) {
+            mNamespace = Preconditions.checkNotNull(namespace);
+        }
+
+        /**
          * Sets the namespace to remove documents for.
          *
-         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         * <p>If this is not set, it defaults to an empty string.
+         *
+         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+         * removed.
          *
          * @throws IllegalStateException if the builder has already been used.
+         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+         *     exists only for dogfooder transition and must
          */
+        @Deprecated
         @NonNull
         public Builder setNamespace(@NonNull String namespace) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index 2bfcf28..2cd08c6 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -62,18 +62,40 @@
 
     /** Builder for {@link ReportUsageRequest} objects. */
     public static final class Builder {
-        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private String mNamespace;
         private String mUri;
         private Long mUsageTimeMillis;
         private boolean mBuilt = false;
 
         /**
+         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+         *
+         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+         *     exists only for dogfooder transition and must be removed.
+         */
+        @Deprecated
+        public Builder() {
+            mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        }
+
+        /** Creates a {@link ReportUsageRequest.Builder} instance. */
+        public Builder(@NonNull String namespace) {
+            mNamespace = Preconditions.checkNotNull(namespace);
+        }
+
+        /**
          * Sets which namespace the document being used belongs to.
          *
-         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         * <p>If this is not set, it defaults to an empty string.
+         *
+         * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
+         * removed.
          *
          * @throws IllegalStateException if the builder has already been used
+         * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
+         *     exists only for dogfooder transition and must
          */
+        @Deprecated
         @NonNull
         public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
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 f34034b..cb20849 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -32,7 +32,7 @@
  * <p>This allows clients to obtain:
  *
  * <ul>
- *   <li>The document which matched, using {@link #getDocument}
+ *   <li>The document which matched, using {@link #getGenericDocument}
  *   <li>Information about which properties in the document matched, and "snippet" information
  *       containing textual summaries of the document's matches, using {@link #getMatches}
  * </ul>
@@ -43,17 +43,10 @@
  * @see SearchResults
  */
 public final class SearchResult {
-    /** @hide */
-    public static final String DOCUMENT_FIELD = "document";
-
-    /** @hide */
-    public static final String MATCHES_FIELD = "matches";
-
-    /** @hide */
-    public static final String PACKAGE_NAME_FIELD = "packageName";
-
-    /** @hide */
-    public static final String DATABASE_NAME_FIELD = "databaseName";
+    static final String DOCUMENT_FIELD = "document";
+    static final String MATCHES_FIELD = "matches";
+    static final String PACKAGE_NAME_FIELD = "packageName";
+    static final String DATABASE_NAME_FIELD = "databaseName";
 
     @NonNull private final Bundle mBundle;
 
@@ -74,13 +67,20 @@
         return mBundle;
     }
 
+    /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+    @NonNull
+    @Deprecated
+    public GenericDocument getDocument() {
+        return getGenericDocument();
+    }
+
     /**
      * Contains the matching {@link GenericDocument}.
      *
      * @return Document object which matched the query.
      */
     @NonNull
-    public GenericDocument getDocument() {
+    public GenericDocument getGenericDocument() {
         if (mDocument == null) {
             mDocument =
                     new GenericDocument(
@@ -104,7 +104,7 @@
                     Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD));
             mMatches = new ArrayList<>(matchBundles.size());
             for (int i = 0; i < matchBundles.size(); i++) {
-                MatchInfo matchInfo = new MatchInfo(getDocument(), matchBundles.get(i));
+                MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument());
                 mMatches.add(matchInfo);
             }
         }
@@ -124,13 +124,69 @@
     /**
      * Contains the database name that stored the {@link GenericDocument}.
      *
-     * @return Database name that stored the document
+     * @return Name of the database within which the document is stored
      */
     @NonNull
     public String getDatabaseName() {
         return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD));
     }
 
+    /** Builder for {@link SearchResult} objects. */
+    public static final class Builder {
+        private final Bundle mBundle = new Bundle();
+        private final ArrayList<Bundle> mMatchInfos = new ArrayList<>();
+
+        private boolean mBuilt;
+
+        /**
+         * Constructs a new builder for {@link SearchResult} objects.
+         *
+         * @param packageName the package name the matched document belongs to
+         * @param databaseName the database name the matched document belongs to.
+         */
+        public Builder(@NonNull String packageName, @NonNull String databaseName) {
+            mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName));
+            mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName));
+        }
+
+        /**
+         * Sets the document which matched.
+         *
+         * @throws IllegalStateException if the builder has already been used
+         */
+        @NonNull
+        public Builder setGenericDocument(@NonNull GenericDocument document) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBundle.putBundle(DOCUMENT_FIELD, document.getBundle());
+            return this;
+        }
+
+        /** Adds another match to this SearchResult. */
+        @NonNull
+        public Builder addMatch(@NonNull MatchInfo matchInfo) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkState(
+                    matchInfo.mDocument == null,
+                    "This MatchInfo is already associated with a SearchResult and can't be "
+                            + "reassigned");
+            mMatchInfos.add(matchInfo.mBundle);
+            return this;
+        }
+
+        /**
+         * Constructs a new {@link SearchResult}.
+         *
+         * @throws IllegalStateException if the builder has already been used
+         */
+        @NonNull
+        public SearchResult build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBundle.putParcelableArrayList(MATCHES_FIELD, mMatchInfos);
+            mBuilt = true;
+            return new SearchResult(mBundle);
+        }
+    }
+
     /**
      * This class represents a match objects for any Snippets that might be present in {@link
      * SearchResults} from query. Using this class user can get the full text, exact matches and
@@ -147,11 +203,11 @@
      * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
      * nonsense word that’s used a lot is bar."
      *
-     * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
+     * <p>{@link MatchInfo#getExactMatchRange()} returns [29, 32]
      *
      * <p>{@link MatchInfo#getExactMatch()} returns "foo"
      *
-     * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33]
+     * <p>{@link MatchInfo#getSnippetRange()} returns [26, 33]
      *
      * <p>{@link MatchInfo#getSnippet()} returns "is foo."
      *
@@ -172,11 +228,11 @@
      *
      * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
      *
-     * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
+     * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 4]
      *
      * <p>{@link MatchInfo#getExactMatch()} returns "Test"
      *
-     * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
+     * <p>{@link MatchInfo#getSnippetRange()} returns [0, 9]
      *
      * <p>{@link MatchInfo#getSnippet()} returns "Test Name"
      *
@@ -186,52 +242,54 @@
      *
      * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
      *
-     * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
+     * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 20]
      *
      * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
      *
-     * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
+     * <p>{@link MatchInfo#getSnippetRange()} returns [0, 20]
      *
      * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
      */
     public static final class MatchInfo {
-        /**
-         * The path of the matching snippet property.
-         *
-         * @hide
-         */
-        public static final String PROPERTY_PATH_FIELD = "propertyPath";
+        /** The path of the matching snippet property. */
+        private static final String PROPERTY_PATH_FIELD = "propertyPath";
 
-        /** @hide */
-        public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
+        private static final String EXACT_MATCH_RANGE_LOWER_FIELD = "exactMatchRangeLower";
+        private static final String EXACT_MATCH_RANGE_UPPER_FIELD = "exactMatchRangeUpper";
+        private static final String SNIPPET_RANGE_LOWER_FIELD = "snippetRangeLower";
+        private static final String SNIPPET_RANGE_UPPER_FIELD = "snippetRangeUpper";
 
-        /** @hide */
-        public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper";
-
-        /** @hide */
-        public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower";
-
-        /** @hide */
-        public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper";
-
-        private final String mFullText;
         private final String mPropertyPath;
-        private final Bundle mBundle;
-        private MatchRange mExactMatchRange;
-        private MatchRange mWindowRange;
+        final Bundle mBundle;
 
-        MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) {
+        /**
+         * Document which the match comes from.
+         *
+         * <p>If this is {@code null}, methods which require access to the document, like {@link
+         * #getExactMatch}, will throw {@link NullPointerException}.
+         */
+        @Nullable final GenericDocument mDocument;
+
+        /** Full text of the matched property. Populated on first use. */
+        @Nullable private String mFullText;
+
+        /** Range of property that exactly matched the query. Populated on first use. */
+        @Nullable private MatchRange mExactMatchRange;
+
+        /** Range of some reasonable amount of context around the query. Populated on first use. */
+        @Nullable private MatchRange mWindowRange;
+
+        MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) {
             mBundle = Preconditions.checkNotNull(bundle);
-            Preconditions.checkNotNull(document);
+            mDocument = document;
             mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
-            mFullText = getPropertyValues(document, mPropertyPath);
         }
 
         /**
          * Gets the property path corresponding to the given entry.
          *
-         * <p>Property Path: '.' - delimited sequence of property names indicating which property in
-         * the Document these snippets correspond to.
+         * <p>A property path is a '.' - delimited sequence of property names indicating which
+         * property in the document these snippets correspond to.
          *
          * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
          * example 1 this returns "subject"
@@ -249,21 +307,34 @@
          */
         @NonNull
         public String getFullText() {
+            if (mFullText == null) {
+                Preconditions.checkState(
+                        mDocument != null,
+                        "Document has not been populated; this MatchInfo cannot be used yet");
+                mFullText = getPropertyValues(mDocument, mPropertyPath);
+            }
             return mFullText;
         }
 
+        /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+        @NonNull
+        @Deprecated
+        public MatchRange getExactMatchPosition() {
+            return getExactMatchRange();
+        }
+
         /**
          * Gets the exact {@link MatchRange} corresponding to the given entry.
          *
          * <p>For class example 1 this returns [29, 32]
          */
         @NonNull
-        public MatchRange getExactMatchPosition() {
+        public MatchRange getExactMatchRange() {
             if (mExactMatchRange == null) {
                 mExactMatchRange =
                         new MatchRange(
-                                mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
-                                mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
+                                mBundle.getInt(EXACT_MATCH_RANGE_LOWER_FIELD),
+                                mBundle.getInt(EXACT_MATCH_RANGE_UPPER_FIELD));
             }
             return mExactMatchRange;
         }
@@ -275,7 +346,14 @@
          */
         @NonNull
         public CharSequence getExactMatch() {
-            return getSubstring(getExactMatchPosition());
+            return getSubstring(getExactMatchRange());
+        }
+
+        /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
+        @NonNull
+        @Deprecated
+        public MatchRange getSnippetPosition() {
+            return getSnippetRange();
         }
 
         /**
@@ -287,12 +365,12 @@
          * <p>For class example 1 this returns [29, 41].
          */
         @NonNull
-        public MatchRange getSnippetPosition() {
+        public MatchRange getSnippetRange() {
             if (mWindowRange == null) {
                 mWindowRange =
                         new MatchRange(
-                                mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
-                                mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
+                                mBundle.getInt(SNIPPET_RANGE_LOWER_FIELD),
+                                mBundle.getInt(SNIPPET_RANGE_UPPER_FIELD));
             }
             return mWindowRange;
         }
@@ -309,7 +387,7 @@
          */
         @NonNull
         public CharSequence getSnippet() {
-            return getSubstring(getSnippetPosition());
+            return getSubstring(getSnippetRange());
         }
 
         private CharSequence getSubstring(MatchRange range) {
@@ -331,6 +409,72 @@
             // TODO(b/175146044): Return the proper match based on the index in the propertyName.
             return values[0];
         }
+
+        /** Builder for {@link MatchInfo} objects. */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /**
+             * Sets the property path corresponding to the given entry.
+             *
+             * <p>A property path is a '.' - delimited sequence of property names indicating which
+             * property in the document these snippets correspond to.
+             *
+             * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
+             * example 1 this returns "subject"
+             *
+             * @throws IllegalStateException if the builder has already been used
+             */
+            @NonNull
+            public Builder setPropertyPath(@NonNull String propertyPath) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                mBundle.putString(
+                        SearchResult.MatchInfo.PROPERTY_PATH_FIELD,
+                        Preconditions.checkNotNull(propertyPath));
+                return this;
+            }
+
+            /**
+             * Sets the exact {@link MatchRange} corresponding to the given entry.
+             *
+             * @throws IllegalStateException if the builder has already been used
+             */
+            @NonNull
+            public Builder setExactMatchRange(@NonNull MatchRange matchRange) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkNotNull(matchRange);
+                mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart());
+                mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd());
+                return this;
+            }
+
+            /**
+             * Sets the snippet {@link MatchRange} corresponding to the given entry.
+             *
+             * @throws IllegalStateException if the builder has already been used
+             */
+            @NonNull
+            public Builder setSnippetRange(@NonNull MatchRange matchRange) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkNotNull(matchRange);
+                mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart());
+                mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd());
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link MatchInfo}.
+             *
+             * @throws IllegalStateException if the builder has already been used
+             */
+            @NonNull
+            public MatchInfo build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                mBuilt = true;
+                return new MatchInfo(mBundle, /*document=*/ null);
+            }
+        }
     }
 
     /**
@@ -353,7 +497,6 @@
          *
          * @param start The start point (inclusive)
          * @param end The end point (exclusive)
-         * @hide
          */
         public MatchRange(int start, int end) {
             if (start > end) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index a146006..98cd49b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -160,12 +160,8 @@
                 .addMigrationFailures(mMigrationFailures);
     }
 
-    /**
-     * Builder for {@link SetSchemaResponse} objects.
-     *
-     * @hide
-     */
-    public static class Builder {
+    /** Builder for {@link SetSchemaResponse} objects. */
+    public static final class Builder {
         private final ArrayList<MigrationFailure> mMigrationFailures = new ArrayList<>();
         private final ArrayList<String> mDeletedTypes = new ArrayList<>();
         private final ArrayList<String> mMigratedTypes = new ArrayList<>();
@@ -309,12 +305,8 @@
                     mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
         }
 
-        /**
-         * Builder for {@link MigrationFailure} objects.
-         *
-         * @hide
-         */
-        public static class Builder {
+        /** Builder for {@link MigrationFailure} objects. */
+        public static final class Builder {
             private String mSchemaType;
             private String mNamespace;
             private String mUri;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
index b1a33a4..ca4ea2b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
@@ -32,19 +32,27 @@
     /**
      * Initializes an {@link AppSearchException} with no message.
      *
-     * @hide
+     * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
      */
     public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
         this(resultCode, /*message=*/ null);
     }
 
-    /** @hide */
+    /**
+     * Initializes an {@link AppSearchException} with a result code and message.
+     *
+     * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+     */
     public AppSearchException(
             @AppSearchResult.ResultCode int resultCode, @Nullable String message) {
         this(resultCode, message, /*cause=*/ null);
     }
 
-    /** @hide */
+    /**
+     * Initializes an {@link AppSearchException} with a result code, message and cause.
+     *
+     * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+     */
     public AppSearchException(
             @AppSearchResult.ResultCode int resultCode,
             @Nullable String message,
@@ -53,12 +61,16 @@
         mResultCode = resultCode;
     }
 
-    /** Returns the result code this exception was constructed with. */
+    /**
+     * Returns the result code this exception was constructed with.
+     *
+     * @return One of the constants documented in {@link AppSearchResult#getResultCode}.
+     */
     public @AppSearchResult.ResultCode int getResultCode() {
         return mResultCode;
     }
 
-    /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */
+    /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}. */
     @NonNull
     public <T> AppSearchResult<T> toAppSearchResult() {
         return AppSearchResult.newFailedResult(mResultCode, getMessage());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
index a7f1cc4..4b8ce6d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
@@ -93,7 +93,7 @@
                             migrator.transform(
                                     currentVersion,
                                     finalVersion,
-                                    searchResultPage.getResults().get(i).getDocument());
+                                    searchResultPage.getResults().get(i).getGenericDocument());
                     Bundle bundle = newDocument.getBundle();
                     Parcel parcel = Parcel.obtain();
                     parcel.writeBundle(bundle);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index a2386ec..d6b9da8 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -102,8 +102,8 @@
     public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) {
         Preconditions.checkNotNull(proto);
         GenericDocument.Builder<?> documentBuilder =
-                new GenericDocument.Builder<>(proto.getUri(), proto.getSchema())
-                        .setNamespace(proto.getNamespace())
+                new GenericDocument.Builder<>(
+                                proto.getNamespace(), proto.getUri(), proto.getSchema())
                         .setScore(proto.getScore())
                         .setTtlMillis(proto.getTtlMs())
                         .setCreationTimestampMillis(proto.getCreationTimestampMs());
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 e9852aa..1d8db72 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
@@ -58,14 +58,14 @@
             @NonNull List<String> databaseNames) {
         Preconditions.checkArgument(
                 proto.getResultsCount() == packageNames.size(),
-                "Size of " + "results does not match the number of package names.");
+                "Size of results does not match the number of package names.");
         Bundle bundle = new Bundle();
         bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken());
         ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
         for (int i = 0; i < proto.getResultsCount(); i++) {
-            resultBundles.add(
-                    toSearchResultBundle(
-                            proto.getResults(i), packageNames.get(i), databaseNames.get(i)));
+            SearchResult result =
+                    toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i));
+            resultBundles.add(result.getBundle());
         }
         bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
         return new SearchResultPage(bundle);
@@ -80,50 +80,41 @@
      * @return A {@link SearchResult} bundle.
      */
     @NonNull
-    private static Bundle toSearchResultBundle(
+    private static SearchResult toSearchResult(
             @NonNull SearchResultProto.ResultProtoOrBuilder proto,
             @NonNull String packageName,
             @NonNull String databaseName) {
-        Bundle bundle = new Bundle();
         GenericDocument document =
                 GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument());
-        bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle());
-        bundle.putString(SearchResult.PACKAGE_NAME_FIELD, packageName);
-        bundle.putString(SearchResult.DATABASE_NAME_FIELD, databaseName);
-
-        ArrayList<Bundle> matchList = new ArrayList<>();
+        SearchResult.Builder builder =
+                new SearchResult.Builder(packageName, databaseName).setGenericDocument(document);
         if (proto.hasSnippet()) {
             for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
                 SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
                 for (int j = 0; j < entry.getSnippetMatchesCount(); j++) {
-                    Bundle matchInfoBundle =
-                            convertToMatchInfoBundle(
-                                    entry.getSnippetMatches(j), entry.getPropertyName());
-                    matchList.add(matchInfoBundle);
+                    SearchResult.MatchInfo matchInfo =
+                            toMatchInfo(entry.getSnippetMatches(j), entry.getPropertyName());
+                    builder.addMatch(matchInfo);
                 }
             }
         }
-        bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList);
-
-        return bundle;
+        return builder.build();
     }
 
-    private static Bundle convertToMatchInfoBundle(
-            SnippetMatchProto snippetMatchProto, String propertyPath) {
-        Bundle bundle = new Bundle();
-        bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
-        bundle.putInt(
-                SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
-                snippetMatchProto.getExactMatchPosition());
-        bundle.putInt(
-                SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD,
-                snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes());
-        bundle.putInt(
-                SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD,
-                snippetMatchProto.getWindowPosition());
-        bundle.putInt(
-                SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD,
-                snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes());
-        return bundle;
+    private static SearchResult.MatchInfo toMatchInfo(
+            @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) {
+        return new SearchResult.MatchInfo.Builder()
+                .setPropertyPath(propertyPath)
+                .setExactMatchRange(
+                        new SearchResult.MatchRange(
+                                snippetMatchProto.getExactMatchPosition(),
+                                snippetMatchProto.getExactMatchPosition()
+                                        + snippetMatchProto.getExactMatchBytes()))
+                .setSnippetRange(
+                        new SearchResult.MatchRange(
+                                snippetMatchProto.getWindowPosition(),
+                                snippetMatchProto.getWindowPosition()
+                                        + snippetMatchProto.getWindowBytes()))
+                .build();
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
new file mode 100644
index 0000000..aeb66d9
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -0,0 +1,315 @@
+/*
+ * 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.appsearch.stats;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.appsearch.external.localstorage.AppSearchLogger;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Logger Implementation using Westworld.
+ *
+ * <p>This class is thread-safe.
+ *
+ * @hide
+ */
+public final class PlatformLogger implements AppSearchLogger {
+    private static final String TAG = "AppSearchPlatformLogger";
+
+    // Context of the system service.
+    private final Context mContext;
+
+    // User ID of the caller who we're logging for.
+    private final int mUserId;
+
+    // Configuration for the logger
+    private final Config mConfig;
+
+    private final Random mRng = new Random();
+    private final Object mLock = new Object();
+
+    /**
+     * SparseArray to track how many stats we skipped due to
+     * {@link Config#mMinTimeIntervalBetweenSamplesMillis}.
+     *
+     * <p> We can have correct extrapolated number by adding those counts back when we log
+     * the same type of stats next time. E.g. the true count of an event could be estimated as:
+     * SUM(sampling_ratio * (num_skipped_sample + 1)) as est_count
+     *
+     * <p>The key to the SparseArray is {@link CallStats.CallType}
+     */
+    @GuardedBy("mLock")
+    private final SparseIntArray mSkippedSampleCountLocked =
+            new SparseIntArray();
+
+    /**
+     * Map to cache the packageUid for each package.
+     *
+     * <p>It maps packageName to packageUid.
+     *
+     * <p>The entry will be removed whenever the app gets uninstalled
+     */
+    @GuardedBy("mLock")
+    private final Map<String, Integer> mPackageUidCacheLocked =
+            new ArrayMap<>();
+
+    /**
+     * Elapsed time for last stats logged from boot in millis
+     */
+    @GuardedBy("mLock")
+    private long mLastPushTimeMillisLocked = 0;
+
+    /**
+     * Class to configure the {@link PlatformLogger}
+     */
+    public static final class Config {
+        // Minimum time interval (in millis) since last message logged to Westworld before
+        // logging again.
+        private final long mMinTimeIntervalBetweenSamplesMillis;
+
+        // Default sampling ratio for all types of stats
+        private final int mDefaultSamplingRatio;
+
+        /**
+         * Sampling ratios for different types of stats
+         *
+         * <p>This SparseArray is passed by client and is READ-ONLY. The key to that SparseArray is
+         * {@link CallStats.CallType}
+         *
+         * <p>If sampling ratio is missing for certain stats type,
+         * {@link Config#mDefaultSamplingRatio} will be used.
+         *
+         * <p>E.g. sampling ratio=10 means that one out of every 10 stats was logged. If sampling
+         * ratio is 1, we will log each sample and it acts as if the sampling is disabled.
+         */
+        @NonNull
+        private final SparseIntArray mSamplingRatios;
+
+        /**
+         * Configuration for {@link PlatformLogger}
+         *
+         * @param minTimeIntervalBetweenSamplesMillis minimum time interval apart in Milliseconds
+         *                                            required for two consecutive stats logged
+         * @param defaultSamplingRatio                default sampling ratio
+         * @param samplingRatios                    SparseArray to customize sampling ratio for
+         *                                            different stat types
+         */
+        public Config(long minTimeIntervalBetweenSamplesMillis,
+                int defaultSamplingRatio,
+                @Nullable SparseIntArray samplingRatios) {
+            mMinTimeIntervalBetweenSamplesMillis = minTimeIntervalBetweenSamplesMillis;
+            mDefaultSamplingRatio = defaultSamplingRatio;
+            if (samplingRatios != null) {
+                mSamplingRatios = samplingRatios;
+            } else {
+                mSamplingRatios = new SparseIntArray();
+            }
+        }
+    }
+
+    /**
+     * Helper class to hold platform specific stats for Westworld.
+     */
+    static final class ExtraStats {
+        // UID for the calling package of the stats.
+        final int mPackageUid;
+        // sampling ratio for the call type of the stats.
+        final int mSamplingRatio;
+        // number of samplings skipped before the current one for the same call type.
+        final int mSkippedSampleCount;
+
+        ExtraStats(int packageUid, int samplingRatio, int skippedSampleCount) {
+            mPackageUid = packageUid;
+            mSamplingRatio = samplingRatio;
+            mSkippedSampleCount = skippedSampleCount;
+        }
+    }
+
+    /**
+     * Westworld constructor
+     */
+    public PlatformLogger(@NonNull Context context, int userId, @NonNull Config config) {
+        mContext = Preconditions.checkNotNull(context);
+        mConfig = Preconditions.checkNotNull(config);
+        mUserId = userId;
+    }
+
+    /** Logs {@link CallStats}. */
+    @Override
+    public void logStats(@NonNull CallStats stats) {
+        Preconditions.checkNotNull(stats);
+        synchronized (mLock) {
+            if (shouldLogForTypeLocked(stats.getCallType())) {
+                logToWestworldLocked(stats);
+            }
+        }
+    }
+
+    /** Logs {@link PutDocumentStats}. */
+    @Override
+    public void logStats(@NonNull PutDocumentStats stats) {
+        Preconditions.checkNotNull(stats);
+        synchronized (mLock) {
+            if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) {
+                logToWestworldLocked(stats);
+            }
+        }
+    }
+
+    /**
+     * Removes cached UID for package.
+     *
+     * @return removed UID for the package, or {@code INVALID_UID} if package was not previously
+     * cached.
+    */
+    public int removeCachedUidForPackage(@NonNull String packageName) {
+        // TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent
+        Preconditions.checkNotNull(packageName);
+        synchronized (mLock) {
+            Integer uid = mPackageUidCacheLocked.remove(packageName);
+            return uid != null ? uid : Process.INVALID_UID;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void logToWestworldLocked(@NonNull CallStats stats) {
+        mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+        ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
+                stats.getCallType());
+        /* TODO(b/173532925) Log the CallStats to Westworld
+        stats.log(..., samplingRatio, skippedSampleCount, ...)
+         */
+    }
+
+    @GuardedBy("mLock")
+    private void logToWestworldLocked(@NonNull PutDocumentStats stats) {
+        mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+        ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
+                CallStats.CALL_TYPE_PUT_DOCUMENT);
+        /* TODO(b/173532925) Log the PutDocumentStats to Westworld
+        stats.log(..., samplingRatio, skippedSampleCount, ...)
+         */
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    @NonNull
+    ExtraStats createExtraStatsLocked(@NonNull String packageName,
+            @CallStats.CallType int callType) {
+        int packageUid = getPackageUidAsUserLocked(packageName);
+        int samplingRatio = mConfig.mSamplingRatios.get(callType,
+                mConfig.mDefaultSamplingRatio);
+
+        int skippedSampleCount = mSkippedSampleCountLocked.get(callType,
+                /*valueOfKeyIfNotFound=*/ 0);
+        mSkippedSampleCountLocked.put(callType, 0);
+
+        return new ExtraStats(packageUid, samplingRatio, skippedSampleCount);
+    }
+
+    /**
+     * Checks if this stats should be logged.
+     *
+     * <p>It won't be logged if it is "sampled" out, or it is too close to the previous logged
+     * stats.
+     */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    boolean shouldLogForTypeLocked(@CallStats.CallType int callType) {
+        int samplingRatio = mConfig.mSamplingRatios.get(callType,
+                mConfig.mDefaultSamplingRatio);
+
+        // Sampling
+        if (!shouldSample(samplingRatio)) {
+            return false;
+        }
+
+        // Rate limiting
+        // Check the timestamp to see if it is too close to last logged sample
+        long currentTimeMillis = SystemClock.elapsedRealtime();
+        if (mLastPushTimeMillisLocked
+                > currentTimeMillis - mConfig.mMinTimeIntervalBetweenSamplesMillis) {
+            int count = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0);
+            ++count;
+            mSkippedSampleCountLocked.put(callType, count);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks if the stats should be "sampled"
+     *
+     * @param samplingRatio sampling ratio
+     * @return if the stats should be sampled
+     */
+    private boolean shouldSample(int samplingRatio) {
+        if (samplingRatio <= 0) {
+            return false;
+        }
+
+        return mRng.nextInt((int) samplingRatio) == 0;
+    }
+
+    /**
+     * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
+     * find the UID.
+     */
+    @GuardedBy("mLock")
+    private int getPackageUidAsUserLocked(@NonNull String packageName) {
+        Integer packageUid = mPackageUidCacheLocked.get(packageName);
+        if (packageUid != null) {
+            return packageUid;
+        }
+
+        // TODO(b/173532925) since VisibilityStore has the same method, we can make this a
+        //  utility function
+        try {
+            packageUid = mContext.getPackageManager().getPackageUidAsUser(packageName, mUserId);
+            mPackageUidCacheLocked.put(packageName, packageUid);
+            return packageUid;
+        } catch (PackageManager.NameNotFoundException e) {
+            // Package doesn't exist, continue
+        }
+        return Process.INVALID_UID;
+    }
+
+    //
+    // Functions below are used for tests only
+    //
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    void setLastPushTimeMillisLocked(long lastPushElapsedTimeMillis) {
+        mLastPushTimeMillisLocked = lastPushElapsedTimeMillis;
+    }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 41c70f0..68531b6 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I42b89416968565ceb6483b400894f5b49524208c
+I1926fb1d13628607f7a513c8149b65dd86c98dd6
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 37717d6..4a3c7a5 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -47,10 +47,7 @@
         AppSearchBatchResult<String, GenericDocument> result =
                 checkIsBatchResultSuccess(
                         session.getByUri(
-                                new GetByUriRequest.Builder()
-                                        .setNamespace(namespace)
-                                        .addUris(uris)
-                                        .build()));
+                                new GetByUriRequest.Builder(namespace).addUris(uris).build()));
         assertThat(result.getSuccesses()).hasSize(uris.length);
         assertThat(result.getFailures()).isEmpty();
         List<GenericDocument> list = new ArrayList<>(uris.length);
@@ -80,7 +77,7 @@
         List<GenericDocument> documents = new ArrayList<>();
         while (results.size() > 0) {
             for (SearchResult result : results) {
-                documents.add(result.getDocument());
+                documents.add(result.getGenericDocument());
             }
             results = searchResults.getNextPage().get();
         }
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
index 4b0f719..999860f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
@@ -42,7 +42,7 @@
             blobStoreManagerInternal.onIdleMaintenance();
             jobFinished(params, false);
         });
-        return false;
+        return true;
     }
 
     @Override
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index b7a3f10..6967d81 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -59,6 +59,9 @@
  * constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an
  * exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is
  * valid to schedule jobs with no constraints.
+ * <p> Prior to Android version {@link Build.VERSION_CODES#S}, jobs could only have a maximum of 100
+ * jobs scheduled at a time. Starting with Android version {@link Build.VERSION_CODES#S}, that limit
+ * has been increased to 150. Expedited jobs also count towards the limit.
  * <p> In Android version {@link Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum execution time
  * of one minute. Starting with Android version {@link Build.VERSION_CODES#M} and ending with
  * Android version {@link Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes.
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
index 0c4fcb4..6e4a5a0 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.os.DeviceIdleManager;
 import android.os.IDeviceIdleController;
+import android.os.PowerExemptionManager;
 import android.os.PowerWhitelistManager;
 
 /**
@@ -52,5 +53,8 @@
         SystemServiceRegistry.registerContextAwareService(
                 Context.POWER_WHITELIST_MANAGER, PowerWhitelistManager.class,
                 PowerWhitelistManager::new);
+        SystemServiceRegistry.registerContextAwareService(
+                Context.POWER_EXEMPTION_SERVICE, PowerExemptionManager.class,
+                PowerExemptionManager::new);
     }
 }
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 752c36e..6cdf585 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -65,7 +65,7 @@
      * @return package names the system has white-listed to opt out of power save restrictions,
      * except for device idle mode.
      *
-     * @hide Should be migrated to PowerWhitelistManager
+     * @hide Should be migrated to PowerExemptionManager
      */
     @TestApi
     public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
@@ -80,7 +80,7 @@
      * @return package names the system has white-listed to opt out of power save restrictions for
      * all modes.
      *
-     * @hide Should be migrated to PowerWhitelistManager
+     * @hide Should be migrated to PowerExemptionManager
      */
     @TestApi
     public @NonNull String[] getSystemPowerWhitelist() {
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 43d4873..9d18dfe 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -42,7 +42,7 @@
     boolean isPowerSaveWhitelistExceptIdleApp(String name);
     boolean isPowerSaveWhitelistApp(String name);
     @UnsupportedAppUsage(maxTargetSdk = 30,
-     publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.")
+     publicAlternatives = "Use SystemApi {@code PowerExemptionManager#addToTemporaryAllowList(String, int, int, String)}.")
     void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason);
     long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason);
     long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason);
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
new file mode 100644
index 0000000..d9a49aa
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -0,0 +1,633 @@
+/*
+ * 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 android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Interface to access and modify the permanent and temporary power save allow list. The two lists
+ * are kept separately. Apps placed on the permanent allow list are only removed via an explicit
+ * {@link #removeFromAllowList(String)} call. Apps allow-listed by default by the system cannot be
+ * removed. Apps placed on the temporary allow list are removed from that allow list after a
+ * predetermined amount of time.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.POWER_EXEMPTION_SERVICE)
+public class PowerExemptionManager {
+    private final Context mContext;
+    // Proxy to DeviceIdleController for now
+    // TODO: migrate to PowerExemptionController
+    private final IDeviceIdleController mService;
+
+    /**
+     * Indicates that an unforeseen event has occurred and the app should be allow-listed to handle
+     * it.
+     */
+    public static final int EVENT_UNSPECIFIED = 0;
+
+    /**
+     * Indicates that an SMS event has occurred and the app should be allow-listed to handle it.
+     */
+    public static final int EVENT_SMS = 1;
+
+    /**
+     * Indicates that an MMS event has occurred and the app should be allow-listed to handle it.
+     */
+    public static final int EVENT_MMS = 2;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EVENT_"}, value = {
+            EVENT_UNSPECIFIED,
+            EVENT_SMS,
+            EVENT_MMS,
+    })
+    public @interface AllowListEvent {
+    }
+
+    /**
+     * Allow the temp allow list behavior, plus allow foreground service start from background.
+     */
+    public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+    /**
+     * Only allow the temp allow list behavior, not allow foreground service start from background.
+     */
+    public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+
+    /**
+     * The list of temp allow list types.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_LIST_TYPE_" }, value = {
+            TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+            TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TempAllowListType {}
+
+    /* Reason codes for BG-FGS-launch. */
+    /**
+     * BG-FGS-launch is denied.
+     * @hide
+     */
+    public static final int REASON_DENIED = -1;
+
+    /* Reason code range 0-9 are reserved for default reasons */
+    /**
+     * The default reason code if reason is unknown.
+     */
+    public static final int REASON_UNKNOWN = 0;
+    /**
+     * Use REASON_OTHER if there is no better choice.
+     */
+    public static final int REASON_OTHER = 1;
+
+    /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT = 10;
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+    /** @hide */
+    public static final int REASON_PROC_STATE_TOP = 12;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BTOP = 13;
+    /** @hide */
+    public static final int REASON_PROC_STATE_FGS = 14;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BFGS = 15;
+
+    /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
+    /** @hide */
+    public static final int REASON_UID_VISIBLE = 50;
+    /** @hide */
+    public static final int REASON_SYSTEM_UID = 51;
+    /** @hide */
+    public static final int REASON_ACTIVITY_STARTER = 52;
+    /** @hide */
+    public static final int REASON_START_ACTIVITY_FLAG = 53;
+    /** @hide */
+    public static final int REASON_FGS_BINDING = 54;
+    /** @hide */
+    public static final int REASON_DEVICE_OWNER = 55;
+    /** @hide */
+    public static final int REASON_PROFILE_OWNER = 56;
+    /** @hide */
+    public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+    /**
+     * START_ACTIVITIES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+    /**
+     * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+    /** @hide */
+    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+    /** @hide */
+    public static final int REASON_DEVICE_DEMO_MODE = 63;
+    /** @hide */
+    public static final int REASON_EXEMPTED_PACKAGE = 64;
+    /** @hide */
+    public static final int REASON_ALLOWLISTED_PACKAGE = 65;
+    /** @hide */
+    public static final int REASON_APPOP = 66;
+
+    /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list.
+       Reason code for temp and system allow list starts here.
+       Reason code range 100-199 are reserved for public reasons. */
+    /**
+     * Set temp-allow-list for location geofence purpose.
+     */
+    public static final int REASON_GEOFENCING = 100;
+    /**
+     * Set temp-allow-list for server push messaging.
+     */
+    public static final int REASON_PUSH_MESSAGING = 101;
+    /**
+     * Set temp-allow-list for server push messaging over the quota.
+     */
+    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+    /**
+     * Set temp-allow-list for activity recognition.
+     */
+    public static final int REASON_ACTIVITY_RECOGNITION = 103;
+    /**
+     * Set temp-allow-list for transferring accounts between users.
+     */
+    public static final int REASON_ACCOUNT_TRANSFER = 104;
+
+    /* Reason code range 200-299 are reserved for broadcast actions */
+    /**
+     * Broadcast ACTION_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_BOOT_COMPLETED = 200;
+    /**
+     * Broadcast ACTION_PRE_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_PRE_BOOT_COMPLETED = 201;
+    /**
+     * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+
+    /* Reason code range 300-399 are reserved for other internal reasons */
+    /**
+     * Device idle system allow list, including EXCEPT-IDLE
+     * @hide
+     */
+    public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
+    /** @hide */
+    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+    /**
+     * AlarmManagerService.
+     * @hide
+     */
+    public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+    /**
+     * ActiveServices.
+     * @hide
+     */
+    public static final int REASON_SERVICE_LAUNCH = 303;
+    /**
+     * KeyChainSystemService.
+     * @hide
+     */
+    public static final int REASON_KEY_CHAIN = 304;
+    /**
+     * PackageManagerService.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_VERIFIER = 305;
+    /**
+     * SyncManager.
+     * @hide
+     */
+    public static final int REASON_SYNC_MANAGER = 306;
+    /**
+     * DomainVerificationProxyV1.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+    /**
+     * DomainVerificationProxyV2.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+    /** @hide */
+    public static final int REASON_VPN = 309;
+    /**
+     * NotificationManagerService.
+     * @hide
+     */
+    public static final int REASON_NOTIFICATION_SERVICE = 310;
+    /**
+     * Broadcast ACTION_MY_PACKAGE_REPLACED.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_REPLACED = 311;
+    /**
+     * LocationProviderManager.
+     * @hide
+     */
+    public static final int REASON_LOCATION_PROVIDER = 312;
+    /**
+     * MediaButtonReceiver.
+     * @hide
+     */
+    public static final int REASON_MEDIA_BUTTON = 313;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_SMS = 314;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_MMS = 315;
+    /**
+     * Shell app.
+     * @hide
+     */
+    public static final int REASON_SHELL = 316;
+
+    /**
+     * The list of BG-FGS-Launch and temp-allow-list reason code.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+            // BG-FGS-Launch reasons.
+            REASON_DENIED,
+            REASON_UNKNOWN,
+            REASON_OTHER,
+            REASON_PROC_STATE_PERSISTENT,
+            REASON_PROC_STATE_PERSISTENT_UI,
+            REASON_PROC_STATE_TOP,
+            REASON_PROC_STATE_BTOP,
+            REASON_PROC_STATE_FGS,
+            REASON_PROC_STATE_BFGS,
+            REASON_UID_VISIBLE,
+            REASON_SYSTEM_UID,
+            REASON_ACTIVITY_STARTER,
+            REASON_START_ACTIVITY_FLAG,
+            REASON_FGS_BINDING,
+            REASON_DEVICE_OWNER,
+            REASON_PROFILE_OWNER,
+            REASON_COMPANION_DEVICE_MANAGER,
+            REASON_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_BACKGROUND_FGS_PERMISSION,
+            REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_INSTR_BACKGROUND_FGS_PERMISSION,
+            REASON_SYSTEM_ALERT_WINDOW_PERMISSION,
+            REASON_DEVICE_DEMO_MODE,
+            REASON_EXEMPTED_PACKAGE,
+            REASON_ALLOWLISTED_PACKAGE,
+            REASON_APPOP,
+            // temp and system allow list reasons.
+            REASON_GEOFENCING,
+            REASON_PUSH_MESSAGING,
+            REASON_PUSH_MESSAGING_OVER_QUOTA,
+            REASON_ACTIVITY_RECOGNITION,
+            REASON_ACCOUNT_TRANSFER,
+            REASON_BOOT_COMPLETED,
+            REASON_PRE_BOOT_COMPLETED,
+            REASON_LOCKED_BOOT_COMPLETED,
+            REASON_SYSTEM_ALLOW_LISTED,
+            REASON_ALARM_MANAGER_ALARM_CLOCK,
+            REASON_ALARM_MANAGER_WHILE_IDLE,
+            REASON_SERVICE_LAUNCH,
+            REASON_KEY_CHAIN,
+            REASON_PACKAGE_VERIFIER,
+            REASON_SYNC_MANAGER,
+            REASON_DOMAIN_VERIFICATION_V1,
+            REASON_DOMAIN_VERIFICATION_V2,
+            REASON_VPN,
+            REASON_NOTIFICATION_SERVICE,
+            REASON_PACKAGE_REPLACED,
+            REASON_LOCATION_PROVIDER,
+            REASON_MEDIA_BUTTON,
+            REASON_EVENT_SMS,
+            REASON_EVENT_MMS,
+            REASON_SHELL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReasonCode {}
+
+    /**
+     * @hide
+     */
+    public PowerExemptionManager(@NonNull Context context) {
+        mContext = context;
+        mService = context.getSystemService(DeviceIdleManager.class).getService();
+    }
+
+    /**
+     * Add the specified package to the permanent power save allow list.
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void addToPermanentAllowList(@NonNull String packageName) {
+        addToPermanentAllowList(Collections.singletonList(packageName));
+    }
+
+    /**
+     * Add the specified packages to the permanent power save allow list.
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void addToPermanentAllowList(@NonNull List<String> packageNames) {
+        try {
+            mService.addPowerSaveWhitelistApps(packageNames);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get a list of app IDs of app that are allow-listed. This does not include temporarily
+     * allow-listed apps.
+     *
+     * @param includingIdle Set to true if the app should be allow-listed from device idle as well
+     *                      as other power save restrictions
+     * @hide
+     */
+    @NonNull
+    public int[] getAllowListedAppIds(boolean includingIdle) {
+        try {
+            if (includingIdle) {
+                return mService.getAppIdWhitelist();
+            } else {
+                return mService.getAppIdWhitelistExceptIdle();
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if the app is allow-listed from power save restrictions. This does not include
+     * temporarily allow-listed apps.
+     *
+     * @param includingIdle Set to true if the app should be allow-listed from device
+     *                      idle as well as other power save restrictions
+     * @hide
+     */
+    public boolean isAllowListed(@NonNull String packageName, boolean includingIdle) {
+        try {
+            if (includingIdle) {
+                return mService.isPowerSaveWhitelistApp(packageName);
+            } else {
+                return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove an app from the permanent power save allow list. Only apps that were added via
+     * {@link #addToPermanentAllowList(String)} or {@link #addToPermanentAllowList(List)} will be
+     * removed. Apps allow-listed by default by the system cannot be removed.
+     *
+     * @param packageName The app to remove from the allow list
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void removeFromAllowList(@NonNull String packageName) {
+        try {
+            mService.removePowerSaveWhitelistApp(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Add an app to the temporary allow list for a short amount of time.
+     *
+     * @param packageName The package to add to the temp allow list
+     * @param durationMs How long to keep the app on the temp allow list for (in milliseconds)
+     * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason a optional human readable reason string, could be null or empty string.
+     */
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public void addToTemporaryAllowList(@NonNull String packageName, long durationMs,
+            @ReasonCode int reasonCode, @Nullable String reason) {
+        try {
+            mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
+                    reasonCode, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Add an app to the temporary allow list for a short amount of time for a specific reason.
+     * The temporary allow list is kept separately from the permanent allow list and apps are
+     * automatically removed from the temporary allow list after a predetermined amount of time.
+     *
+     * @param packageName The package to add to the temp allow list
+     * @param event       The reason to add the app to the temp allow list
+     * @param reasonCode  one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason      A human-readable reason explaining why the app is temp allow-listed. Only
+     *                    used for logging purposes. Could be null or empty string.
+     * @return The duration (in milliseconds) that the app is allow-listed for
+     */
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public long addToTemporaryAllowListForEvent(@NonNull String packageName,
+            @AllowListEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
+        try {
+            switch (event) {
+                case EVENT_MMS:
+                    return mService.addPowerSaveTempWhitelistAppForMms(
+                            packageName, mContext.getUserId(), reasonCode, reason);
+                case EVENT_SMS:
+                    return mService.addPowerSaveTempWhitelistAppForSms(
+                            packageName, mContext.getUserId(), reasonCode, reason);
+                case EVENT_UNSPECIFIED:
+                default:
+                    return mService.whitelistAppTemporarily(
+                            packageName, mContext.getUserId(), reasonCode, reason);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static @ReasonCode int getReasonCodeFromProcState(int procState) {
+        if (procState <= PROCESS_STATE_PERSISTENT) {
+            return REASON_PROC_STATE_PERSISTENT;
+        } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
+            return REASON_PROC_STATE_PERSISTENT_UI;
+        } else if (procState <= PROCESS_STATE_TOP) {
+            return REASON_PROC_STATE_TOP;
+        } else if (procState <= PROCESS_STATE_BOUND_TOP) {
+            return REASON_PROC_STATE_BTOP;
+        } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+            return REASON_PROC_STATE_FGS;
+        } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+            return REASON_PROC_STATE_BFGS;
+        } else {
+            return REASON_DENIED;
+        }
+    }
+
+    /**
+     * Return string name of the integer reason code.
+     * @hide
+     * @param reasonCode
+     * @return string name of the reason code.
+     */
+    public static String reasonCodeToString(@ReasonCode int reasonCode) {
+        switch (reasonCode) {
+            case REASON_DENIED:
+                return "DENIED";
+            case REASON_UNKNOWN:
+                return "UNKNOWN";
+            case REASON_OTHER:
+                return "OTHER";
+            case REASON_PROC_STATE_PERSISTENT:
+                return "PROC_STATE_PERSISTENT";
+            case REASON_PROC_STATE_PERSISTENT_UI:
+                return "PROC_STATE_PERSISTENT_UI";
+            case REASON_PROC_STATE_TOP:
+                return "PROC_STATE_TOP";
+            case REASON_PROC_STATE_BTOP:
+                return "PROC_STATE_BTOP";
+            case REASON_PROC_STATE_FGS:
+                return "PROC_STATE_FGS";
+            case REASON_PROC_STATE_BFGS:
+                return "PROC_STATE_BFGS";
+            case REASON_UID_VISIBLE:
+                return "UID_VISIBLE";
+            case REASON_SYSTEM_UID:
+                return "SYSTEM_UID";
+            case REASON_ACTIVITY_STARTER:
+                return "ACTIVITY_STARTER";
+            case REASON_START_ACTIVITY_FLAG:
+                return "START_ACTIVITY_FLAG";
+            case REASON_FGS_BINDING:
+                return "FGS_BINDING";
+            case REASON_DEVICE_OWNER:
+                return "DEVICE_OWNER";
+            case REASON_PROFILE_OWNER:
+                return "PROFILE_OWNER";
+            case REASON_COMPANION_DEVICE_MANAGER:
+                return "COMPANION_DEVICE_MANAGER";
+            case REASON_BACKGROUND_ACTIVITY_PERMISSION:
+                return "BACKGROUND_ACTIVITY_PERMISSION";
+            case REASON_BACKGROUND_FGS_PERMISSION:
+                return "BACKGROUND_FGS_PERMISSION";
+            case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
+                return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
+            case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
+                return "INSTR_BACKGROUND_FGS_PERMISSION";
+            case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
+                return "SYSTEM_ALERT_WINDOW_PERMISSION";
+            case REASON_DEVICE_DEMO_MODE:
+                return "DEVICE_DEMO_MODE";
+            case REASON_EXEMPTED_PACKAGE:
+                return "EXEMPTED_PACKAGE";
+            case REASON_ALLOWLISTED_PACKAGE:
+                return "ALLOWLISTED_PACKAGE";
+            case REASON_APPOP:
+                return "APPOP";
+            case REASON_GEOFENCING:
+                return "GEOFENCING";
+            case REASON_PUSH_MESSAGING:
+                return "PUSH_MESSAGING";
+            case REASON_PUSH_MESSAGING_OVER_QUOTA:
+                return "PUSH_MESSAGING_OVER_QUOTA";
+            case REASON_ACTIVITY_RECOGNITION:
+                return "ACTIVITY_RECOGNITION";
+            case REASON_ACCOUNT_TRANSFER:
+                return "REASON_ACCOUNT_TRANSFER";
+            case REASON_BOOT_COMPLETED:
+                return "BOOT_COMPLETED";
+            case REASON_PRE_BOOT_COMPLETED:
+                return "PRE_BOOT_COMPLETED";
+            case REASON_LOCKED_BOOT_COMPLETED:
+                return "LOCKED_BOOT_COMPLETED";
+            case REASON_SYSTEM_ALLOW_LISTED:
+                return "SYSTEM_ALLOW_LISTED";
+            case REASON_ALARM_MANAGER_ALARM_CLOCK:
+                return "ALARM_MANAGER_ALARM_CLOCK";
+            case REASON_ALARM_MANAGER_WHILE_IDLE:
+                return "ALARM_MANAGER_WHILE_IDLE";
+            case REASON_SERVICE_LAUNCH:
+                return "SERVICE_LAUNCH";
+            case REASON_KEY_CHAIN:
+                return "KEY_CHAIN";
+            case REASON_PACKAGE_VERIFIER:
+                return "PACKAGE_VERIFIER";
+            case REASON_SYNC_MANAGER:
+                return "SYNC_MANAGER";
+            case REASON_DOMAIN_VERIFICATION_V1:
+                return "DOMAIN_VERIFICATION_V1";
+            case REASON_DOMAIN_VERIFICATION_V2:
+                return "DOMAIN_VERIFICATION_V2";
+            case REASON_VPN:
+                return "VPN";
+            case REASON_NOTIFICATION_SERVICE:
+                return "NOTIFICATION_SERVICE";
+            case REASON_PACKAGE_REPLACED:
+                return "PACKAGE_REPLACED";
+            case REASON_LOCATION_PROVIDER:
+                return "LOCATION_PROVIDER";
+            case REASON_MEDIA_BUTTON:
+                return "MEDIA_BUTTON";
+            case REASON_EVENT_SMS:
+                return "EVENT_SMS";
+            case REASON_EVENT_MMS:
+                return "EVENT_MMS";
+            case REASON_SHELL:
+                return "SHELL";
+            default:
+                return "(unknown:" + reasonCode + ")";
+        }
+    }
+}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index b1b733a..eba39c7 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -16,13 +16,6 @@
 
 package android.os;
 
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
-import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-import static android.app.ActivityManager.PROCESS_STATE_TOP;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,7 +26,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -43,9 +35,11 @@
  * placed on the temporary whitelist are removed from that whitelist after a predetermined amount of
  * time.
  *
+ * @deprecated Use {@link PowerExemptionManager} instead
  * @hide
  */
 @SystemApi
+@Deprecated
 @SystemService(Context.POWER_WHITELIST_MANAGER)
 public class PowerWhitelistManager {
     private final Context mContext;
@@ -53,21 +47,23 @@
     // TODO: migrate to PowerWhitelistController
     private final IDeviceIdleController mService;
 
+    private final PowerExemptionManager mPowerExemptionManager;
+
     /**
      * Indicates that an unforeseen event has occurred and the app should be whitelisted to handle
      * it.
      */
-    public static final int EVENT_UNSPECIFIED = 0;
+    public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED;
 
     /**
      * Indicates that an SMS event has occurred and the app should be whitelisted to handle it.
      */
-    public static final int EVENT_SMS = 1;
+    public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS;
 
     /**
      * Indicates that an MMS event has occurred and the app should be whitelisted to handle it.
      */
-    public static final int EVENT_MMS = 2;
+    public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS;
 
     /**
      * @hide
@@ -84,12 +80,14 @@
     /**
      * Allow the temp allowlist behavior, plus allow foreground service start from background.
      */
-    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
     /**
      * Only allow the temp allowlist behavior, not allow foreground service start from
      * background.
      */
-    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
 
     /**
      * The list of temp allowlist types.
@@ -107,73 +105,83 @@
      * BG-FGS-launch is denied.
      * @hide
      */
-    public static final int REASON_DENIED = -1;
+    public static final int REASON_DENIED = PowerExemptionManager.REASON_DENIED;
 
     /* Reason code range 0-9 are reserved for default reasons */
     /**
      * The default reason code if reason is unknown.
      */
-    public static final int REASON_UNKNOWN = 0;
+    public static final int REASON_UNKNOWN = PowerExemptionManager.REASON_UNKNOWN;
     /**
      * Use REASON_OTHER if there is no better choice.
      */
-    public static final int REASON_OTHER = 1;
+    public static final int REASON_OTHER = PowerExemptionManager.REASON_OTHER;
 
     /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
     /** @hide */
-    public static final int REASON_PROC_STATE_PERSISTENT = 10;
+    public static final int REASON_PROC_STATE_PERSISTENT =
+            PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
     /** @hide */
-    public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+    public static final int REASON_PROC_STATE_PERSISTENT_UI =
+            PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
     /** @hide */
-    public static final int REASON_PROC_STATE_TOP = 12;
+    public static final int REASON_PROC_STATE_TOP = PowerExemptionManager.REASON_PROC_STATE_TOP;
     /** @hide */
-    public static final int REASON_PROC_STATE_BTOP = 13;
+    public static final int REASON_PROC_STATE_BTOP = PowerExemptionManager.REASON_PROC_STATE_BTOP;
     /** @hide */
-    public static final int REASON_PROC_STATE_FGS = 14;
+    public static final int REASON_PROC_STATE_FGS = PowerExemptionManager.REASON_PROC_STATE_FGS;
     /** @hide */
-    public static final int REASON_PROC_STATE_BFGS = 15;
+    public static final int REASON_PROC_STATE_BFGS = PowerExemptionManager.REASON_PROC_STATE_BFGS;
 
     /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
     /** @hide */
-    public static final int REASON_UID_VISIBLE = 50;
+    public static final int REASON_UID_VISIBLE = PowerExemptionManager.REASON_UID_VISIBLE;
     /** @hide */
-    public static final int REASON_SYSTEM_UID = 51;
+    public static final int REASON_SYSTEM_UID = PowerExemptionManager.REASON_SYSTEM_UID;
     /** @hide */
-    public static final int REASON_ACTIVITY_STARTER = 52;
+    public static final int REASON_ACTIVITY_STARTER = PowerExemptionManager.REASON_ACTIVITY_STARTER;
     /** @hide */
-    public static final int REASON_START_ACTIVITY_FLAG = 53;
+    public static final int REASON_START_ACTIVITY_FLAG =
+            PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
     /** @hide */
-    public static final int REASON_FGS_BINDING = 54;
+    public static final int REASON_FGS_BINDING = PowerExemptionManager.REASON_FGS_BINDING;
     /** @hide */
-    public static final int REASON_DEVICE_OWNER = 55;
+    public static final int REASON_DEVICE_OWNER = PowerExemptionManager.REASON_DEVICE_OWNER;
     /** @hide */
-    public static final int REASON_PROFILE_OWNER = 56;
+    public static final int REASON_PROFILE_OWNER = PowerExemptionManager.REASON_PROFILE_OWNER;
     /** @hide */
-    public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+    public static final int REASON_COMPANION_DEVICE_MANAGER =
+            PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
     /**
      * START_ACTIVITIES_FROM_BACKGROUND permission.
      * @hide
      */
-    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION =
+            PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
     /**
      * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
      * @hide
      */
-    public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+    public static final int REASON_BACKGROUND_FGS_PERMISSION =
+            PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION;
     /** @hide */
-    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION =
+            PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
     /** @hide */
-    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION =
+            PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
     /** @hide */
-    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION =
+            PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
     /** @hide */
-    public static final int REASON_DEVICE_DEMO_MODE = 63;
+    public static final int REASON_DEVICE_DEMO_MODE = PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
     /** @hide */
-    public static final int REASON_EXEMPTED_PACKAGE = 64;
+    public static final int REASON_EXEMPTED_PACKAGE = PowerExemptionManager.REASON_EXEMPTED_PACKAGE;
     /** @hide */
-    public static final int REASON_ALLOWLISTED_PACKAGE  = 65;
+    public static final int REASON_ALLOWLISTED_PACKAGE =
+            PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
     /** @hide */
-    public static final int REASON_APPOP = 66;
+    public static final int REASON_APPOP = PowerExemptionManager.REASON_APPOP;
 
     /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
        Reason code for temp and system allowlist starts here.
@@ -181,117 +189,128 @@
     /**
      * Set temp-allowlist for location geofence purpose.
      */
-    public static final int REASON_GEOFENCING = 100;
+    public static final int REASON_GEOFENCING = PowerExemptionManager.REASON_GEOFENCING;
     /**
      * Set temp-allowlist for server push messaging.
      */
-    public static final int REASON_PUSH_MESSAGING = 101;
+    public static final int REASON_PUSH_MESSAGING = PowerExemptionManager.REASON_PUSH_MESSAGING;
     /**
      * Set temp-allowlist for server push messaging over the quota.
      */
-    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA =
+            PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA;
     /**
      * Set temp-allowlist for activity recognition.
      */
-    public static final int REASON_ACTIVITY_RECOGNITION = 103;
+    public static final int REASON_ACTIVITY_RECOGNITION =
+            PowerExemptionManager.REASON_ACTIVITY_RECOGNITION;
 
     /* Reason code range 200-299 are reserved for broadcast actions */
     /**
      * Broadcast ACTION_BOOT_COMPLETED.
      * @hide
      */
-    public static final int REASON_BOOT_COMPLETED = 200;
+    public static final int REASON_BOOT_COMPLETED = PowerExemptionManager.REASON_BOOT_COMPLETED;
     /**
      * Broadcast ACTION_PRE_BOOT_COMPLETED.
      * @hide
      */
-    public static final int REASON_PRE_BOOT_COMPLETED = 201;
+    public static final int REASON_PRE_BOOT_COMPLETED =
+            PowerExemptionManager.REASON_PRE_BOOT_COMPLETED;
     /**
      * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
      * @hide
      */
-    public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+    public static final int REASON_LOCKED_BOOT_COMPLETED =
+            PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
 
     /* Reason code range 300-399 are reserved for other internal reasons */
     /**
      * Device idle system allowlist, including EXCEPT-IDLE
      * @hide
      */
-    public static final int REASON_SYSTEM_ALLOW_LISTED  = 300;
+    public static final int REASON_SYSTEM_ALLOW_LISTED =
+            PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
     /** @hide */
-    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK =
+            PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
     /**
      * AlarmManagerService.
      * @hide
      */
-    public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+    public static final int REASON_ALARM_MANAGER_WHILE_IDLE =
+            PowerExemptionManager.REASON_ALARM_MANAGER_WHILE_IDLE;
     /**
      * ActiveServices.
      * @hide
      */
-    public static final int REASON_SERVICE_LAUNCH = 303;
+    public static final int REASON_SERVICE_LAUNCH = PowerExemptionManager.REASON_SERVICE_LAUNCH;
     /**
      * KeyChainSystemService.
      * @hide
      */
-    public static final int REASON_KEY_CHAIN = 304;
+    public static final int REASON_KEY_CHAIN = PowerExemptionManager.REASON_KEY_CHAIN;
     /**
      * PackageManagerService.
      * @hide
      */
-    public static final int REASON_PACKAGE_VERIFIER = 305;
+    public static final int REASON_PACKAGE_VERIFIER = PowerExemptionManager.REASON_PACKAGE_VERIFIER;
     /**
      * SyncManager.
      * @hide
      */
-    public static final int REASON_SYNC_MANAGER = 306;
+    public static final int REASON_SYNC_MANAGER = PowerExemptionManager.REASON_SYNC_MANAGER;
     /**
      * DomainVerificationProxyV1.
      * @hide
      */
-    public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+    public static final int REASON_DOMAIN_VERIFICATION_V1 =
+            PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V1;
     /**
      * DomainVerificationProxyV2.
      * @hide
      */
-    public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+    public static final int REASON_DOMAIN_VERIFICATION_V2 =
+            PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V2;
     /** @hide */
     public static final int REASON_VPN = 309;
     /**
      * NotificationManagerService.
      * @hide
      */
-    public static final int REASON_NOTIFICATION_SERVICE = 310;
+    public static final int REASON_NOTIFICATION_SERVICE =
+            PowerExemptionManager.REASON_NOTIFICATION_SERVICE;
     /**
      * Broadcast ACTION_MY_PACKAGE_REPLACED.
      * @hide
      */
-    public static final int REASON_PACKAGE_REPLACED = 311;
+    public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED;
     /**
      * LocationProviderManager.
      * @hide
      */
-    public static final int REASON_LOCATION_PROVIDER = 312;
+    public static final int REASON_LOCATION_PROVIDER =
+            PowerExemptionManager.REASON_LOCATION_PROVIDER;
     /**
      * MediaButtonReceiver.
      * @hide
      */
-    public static final int REASON_MEDIA_BUTTON = 313;
+    public static final int REASON_MEDIA_BUTTON = PowerExemptionManager.REASON_MEDIA_BUTTON;
     /**
      * InboundSmsHandler.
      * @hide
      */
-    public static final int REASON_EVENT_SMS = 314;
+    public static final int REASON_EVENT_SMS = PowerExemptionManager.REASON_EVENT_SMS;
     /**
      * InboundSmsHandler.
      * @hide
      */
-    public static final int REASON_EVENT_MMS = 315;
+    public static final int REASON_EVENT_MMS = PowerExemptionManager.REASON_EVENT_MMS;
     /**
      * Shell app.
      * @hide
      */
-    public static final int REASON_SHELL = 316;
+    public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL;
 
     /**
      * The list of BG-FGS-Launch and temp-allowlist reason code.
@@ -360,26 +379,29 @@
     public PowerWhitelistManager(@NonNull Context context) {
         mContext = context;
         mService = context.getSystemService(DeviceIdleManager.class).getService();
+        mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
     }
 
     /**
      * Add the specified package to the permanent power save whitelist.
+     *
+     * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void addToWhitelist(@NonNull String packageName) {
-        addToWhitelist(Collections.singletonList(packageName));
+        mPowerExemptionManager.addToPermanentAllowList(packageName);
     }
 
     /**
      * Add the specified packages to the permanent power save whitelist.
+     *
+     * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void addToWhitelist(@NonNull List<String> packageNames) {
-        try {
-            mService.addPowerSaveWhitelistApps(packageNames);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mPowerExemptionManager.addToPermanentAllowList(packageNames);
     }
 
     /**
@@ -388,19 +410,13 @@
      *
      * @param includingIdle Set to true if the app should be whitelisted from device idle as well
      *                      as other power save restrictions
+     * @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead
      * @hide
      */
+    @Deprecated
     @NonNull
     public int[] getWhitelistedAppIds(boolean includingIdle) {
-        try {
-            if (includingIdle) {
-                return mService.getAppIdWhitelist();
-            } else {
-                return mService.getAppIdWhitelistExceptIdle();
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mPowerExemptionManager.getAllowListedAppIds(includingIdle);
     }
 
     /**
@@ -409,18 +425,12 @@
      *
      * @param includingIdle Set to true if the app should be whitelisted from device
      *                      idle as well as other power save restrictions
+     * @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead
      * @hide
      */
+    @Deprecated
     public boolean isWhitelisted(@NonNull String packageName, boolean includingIdle) {
-        try {
-            if (includingIdle) {
-                return mService.isPowerSaveWhitelistApp(packageName);
-            } else {
-                return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mPowerExemptionManager.isAllowListed(packageName, includingIdle);
     }
 
     /**
@@ -429,14 +439,12 @@
      * whitelisted by default by the system cannot be removed.
      *
      * @param packageName The app to remove from the whitelist
+     * @deprecated Use {@link PowerExemptionManager#removeFromAllowList(String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void removeFromWhitelist(@NonNull String packageName) {
-        try {
-            mService.removePowerSaveWhitelistApp(packageName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mPowerExemptionManager.removeFromAllowList(packageName);
     }
 
     /**
@@ -446,16 +454,14 @@
      * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
      * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
      * @param reason a optional human readable reason string, could be null or empty string.
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+     *             String, long, int, String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
             @ReasonCode int reasonCode, @Nullable String reason) {
-        try {
-            mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
-                    reasonCode, reason);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mPowerExemptionManager.addToTemporaryAllowList(packageName, durationMs, reasonCode, reason);
     }
 
     /**
@@ -463,12 +469,14 @@
      *
      * @param packageName The package to add to the temp whitelist
      * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
-     * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+     *             String, long, int, String)} instead
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
-        whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName);
+        mPowerExemptionManager.addToTemporaryAllowList(
+                packageName, durationMs, REASON_UNKNOWN, packageName);
     }
 
     /**
@@ -481,13 +489,15 @@
      * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
      *                    used for logging purposes. Could be null or empty string.
      * @return The duration (in milliseconds) that the app is whitelisted for
-     * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+     *             String, int, int, String)} instead
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
             @WhitelistEvent int event, @Nullable String reason) {
-        return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason);
+        return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+                packageName, event, REASON_UNKNOWN, reason);
     }
 
     /**
@@ -501,47 +511,25 @@
      * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
      *                    used for logging purposes. Could be null or empty string.
      * @return The duration (in milliseconds) that the app is whitelisted for
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+     *             String, int, int, String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
             @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
-        try {
-            switch (event) {
-                case EVENT_MMS:
-                    return mService.addPowerSaveTempWhitelistAppForMms(
-                            packageName, mContext.getUserId(), reasonCode, reason);
-                case EVENT_SMS:
-                    return mService.addPowerSaveTempWhitelistAppForSms(
-                            packageName, mContext.getUserId(), reasonCode, reason);
-                case EVENT_UNSPECIFIED:
-                default:
-                    return mService.whitelistAppTemporarily(
-                            packageName, mContext.getUserId(), reasonCode, reason);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+                packageName, event, reasonCode, reason);
     }
 
     /**
      * @hide
+     *
+     * @deprecated Use {@link PowerExemptionManager#getReasonCodeFromProcState(int)} instead
      */
+    @Deprecated
     public static @ReasonCode int getReasonCodeFromProcState(int procState) {
-        if (procState <= PROCESS_STATE_PERSISTENT) {
-            return REASON_PROC_STATE_PERSISTENT;
-        } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
-            return REASON_PROC_STATE_PERSISTENT_UI;
-        } else if (procState <= PROCESS_STATE_TOP) {
-            return REASON_PROC_STATE_TOP;
-        } else if (procState <= PROCESS_STATE_BOUND_TOP) {
-            return REASON_PROC_STATE_BTOP;
-        } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
-            return REASON_PROC_STATE_FGS;
-        } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
-            return REASON_PROC_STATE_BFGS;
-        } else {
-            return REASON_DENIED;
-        }
+        return PowerExemptionManager.getReasonCodeFromProcState(procState);
     }
 
     /**
@@ -549,111 +537,10 @@
      * @hide
      * @param reasonCode
      * @return string name of the reason code.
+     * @deprecated Use {@link PowerExemptionManager#reasonCodeToString(int)} instead
      */
+    @Deprecated
     public static String reasonCodeToString(@ReasonCode int reasonCode) {
-        switch (reasonCode) {
-            case REASON_DENIED:
-                return "DENIED";
-            case REASON_UNKNOWN:
-                return "UNKNOWN";
-            case REASON_OTHER:
-                return "OTHER";
-            case REASON_PROC_STATE_PERSISTENT:
-                return "PROC_STATE_PERSISTENT";
-            case REASON_PROC_STATE_PERSISTENT_UI:
-                return "PROC_STATE_PERSISTENT_UI";
-            case REASON_PROC_STATE_TOP:
-                return "PROC_STATE_TOP";
-            case REASON_PROC_STATE_BTOP:
-                return "PROC_STATE_BTOP";
-            case REASON_PROC_STATE_FGS:
-                return "PROC_STATE_FGS";
-            case REASON_PROC_STATE_BFGS:
-                return "PROC_STATE_BFGS";
-            case REASON_UID_VISIBLE:
-                return "UID_VISIBLE";
-            case REASON_SYSTEM_UID:
-                return "SYSTEM_UID";
-            case REASON_ACTIVITY_STARTER:
-                return "ACTIVITY_STARTER";
-            case REASON_START_ACTIVITY_FLAG:
-                return "START_ACTIVITY_FLAG";
-            case REASON_FGS_BINDING:
-                return "FGS_BINDING";
-            case REASON_DEVICE_OWNER:
-                return "DEVICE_OWNER";
-            case REASON_PROFILE_OWNER:
-                return "PROFILE_OWNER";
-            case REASON_COMPANION_DEVICE_MANAGER:
-                return "COMPANION_DEVICE_MANAGER";
-            case REASON_BACKGROUND_ACTIVITY_PERMISSION:
-                return "BACKGROUND_ACTIVITY_PERMISSION";
-            case REASON_BACKGROUND_FGS_PERMISSION:
-                return "BACKGROUND_FGS_PERMISSION";
-            case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
-                return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
-            case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
-                return "INSTR_BACKGROUND_FGS_PERMISSION";
-            case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
-                return "SYSTEM_ALERT_WINDOW_PERMISSION";
-            case REASON_DEVICE_DEMO_MODE:
-                return "DEVICE_DEMO_MODE";
-            case REASON_EXEMPTED_PACKAGE:
-                return "EXEMPTED_PACKAGE";
-            case REASON_ALLOWLISTED_PACKAGE:
-                return "ALLOWLISTED_PACKAGE";
-            case REASON_APPOP:
-                return "APPOP";
-            case REASON_GEOFENCING:
-                return "GEOFENCING";
-            case REASON_PUSH_MESSAGING:
-                return "PUSH_MESSAGING";
-            case REASON_PUSH_MESSAGING_OVER_QUOTA:
-                return "PUSH_MESSAGING_OVER_QUOTA";
-            case REASON_ACTIVITY_RECOGNITION:
-                return "ACTIVITY_RECOGNITION";
-            case REASON_BOOT_COMPLETED:
-                return "BOOT_COMPLETED";
-            case REASON_PRE_BOOT_COMPLETED:
-                return "PRE_BOOT_COMPLETED";
-            case REASON_LOCKED_BOOT_COMPLETED:
-                return "LOCKED_BOOT_COMPLETED";
-            case REASON_SYSTEM_ALLOW_LISTED:
-                return "SYSTEM_ALLOW_LISTED";
-            case REASON_ALARM_MANAGER_ALARM_CLOCK:
-                return "ALARM_MANAGER_ALARM_CLOCK";
-            case REASON_ALARM_MANAGER_WHILE_IDLE:
-                return "ALARM_MANAGER_WHILE_IDLE";
-            case REASON_SERVICE_LAUNCH:
-                return "SERVICE_LAUNCH";
-            case REASON_KEY_CHAIN:
-                return "KEY_CHAIN";
-            case REASON_PACKAGE_VERIFIER:
-                return "PACKAGE_VERIFIER";
-            case REASON_SYNC_MANAGER:
-                return "SYNC_MANAGER";
-            case REASON_DOMAIN_VERIFICATION_V1:
-                return "DOMAIN_VERIFICATION_V1";
-            case REASON_DOMAIN_VERIFICATION_V2:
-                return "DOMAIN_VERIFICATION_V2";
-            case REASON_VPN:
-                return "VPN";
-            case REASON_NOTIFICATION_SERVICE:
-                return "NOTIFICATION_SERVICE";
-            case REASON_PACKAGE_REPLACED:
-                return "PACKAGE_REPLACED";
-            case REASON_LOCATION_PROVIDER:
-                return "LOCATION_PROVIDER";
-            case REASON_MEDIA_BUTTON:
-                return "MEDIA_BUTTON";
-            case REASON_EVENT_SMS:
-                return "EVENT_SMS";
-            case REASON_EVENT_MMS:
-                return "EVENT_MMS";
-            case REASON_SHELL:
-                return "SHELL";
-            default:
-                return  "(unknown:" + reasonCode + ")";
-        }
+        return PowerExemptionManager.reasonCodeToString(reasonCode);
     }
 }
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 515cb74..82f2f69 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -147,10 +147,8 @@
 
     /** The maximum number of concurrent jobs we run at one time. */
     static final int MAX_JOB_CONTEXTS_COUNT = 16;
-    /** Enforce a per-app limit on scheduled jobs? */
-    private static final boolean ENFORCE_MAX_JOBS = true;
-    /** The maximum number of jobs that we allow an unprivileged app to schedule */
-    private static final int MAX_JOBS_PER_APP = 100;
+    /** The maximum number of jobs that we allow an app to schedule */
+    private static final int MAX_JOBS_PER_APP = 150;
     /** The number of the most recently completed jobs to keep track of for debugging purposes. */
     private static final int NUM_COMPLETED_JOB_HISTORY = 20;
 
@@ -1011,7 +1009,7 @@
 
             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
             // Jobs on behalf of others don't apply to the per-app job cap
-            if (ENFORCE_MAX_JOBS && packageName == null) {
+            if (packageName == null) {
                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
                     Slog.w(TAG, "Too many jobs for uid " + uId);
                     throw new IllegalStateException("Apps may not schedule more than "
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index b70e68b..2f3ac22 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -813,12 +813,21 @@
         //   1. it's already running (already executing expedited jobs should be allowed to finish)
         //   2. the app is currently in the foreground
         //   3. the app overall is within its quota
+        //   4. It's on the temp allowlist (or within the grace period)
         if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
             return true;
         }
+        final long tempAllowlistGracePeriodEndElapsed =
+                mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
+        final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
+                || sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed;
+        if (hasTempAllowlistExemption) {
+            return true;
+        }
+
         Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
                 jobStatus.getSourcePackageName());
-        // Any already executing expedited jbos should be allowed to finish.
+        // Any already executing expedited jobs should be allowed to finish.
         if (ejTimer != null && ejTimer.isRunning(jobStatus)) {
             return true;
         }
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 906071f..3f30d3e 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -19,11 +19,14 @@
 import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
 
+import com.android.modules.annotation.MinSdk;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -77,6 +80,7 @@
  There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
  application will only need to specify individual types they supported.
  */
+@MinSdk(Build.VERSION_CODES.S)
 public final class ApplicationMediaCapabilities implements Parcelable {
     private static final String TAG = "ApplicationMediaCapabilities";
 
diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java
index 0e461888..8d1b159 100644
--- a/apex/media/framework/java/android/media/MediaFeature.java
+++ b/apex/media/framework/java/android/media/MediaFeature.java
@@ -17,6 +17,9 @@
 package android.media;
 
 import android.annotation.StringDef;
+import android.os.Build;
+
+import com.android.modules.annotation.MinSdk;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -24,6 +27,7 @@
 /**
  * MediaFeature defines various media features, e.g. hdr type.
  */
+@MinSdk(Build.VERSION_CODES.S)
 public final class MediaFeature {
      /**
      * Defines tye type of HDR(high dynamic range) video.
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index 9332835..de2924e 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -21,7 +21,9 @@
 import android.annotation.SystemApi.Client;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
+import android.os.Build;
 
+import com.android.modules.annotation.MinSdk;
 import com.android.modules.utils.build.SdkLevel;
 
 /**
@@ -29,6 +31,7 @@
  *
  * @hide
  */
+@MinSdk(Build.VERSION_CODES.S)
 @SystemApi(client = Client.MODULE_LIBRARIES)
 public class MediaFrameworkInitializer {
     private MediaFrameworkInitializer() {
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 30d1896..84332e5 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.net.Uri;
+import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -34,6 +35,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.annotation.MinSdk;
 
 import java.io.FileNotFoundException;
 import java.lang.annotation.Retention;
@@ -99,6 +101,7 @@
  TODO(hkuang): Clarify whether supports framerate conversion.
  @hide
  */
+@MinSdk(Build.VERSION_CODES.S)
 @SystemApi
 public final class MediaTranscodeManager {
     private static final String TAG = "MediaTranscodeManager";
diff --git a/api/Android.bp b/api/Android.bp
index 1d4698e..1fdf177 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -347,3 +347,49 @@
     out: ["combined-removed-dex.txt"],
     cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
 }
+
+genrule {
+    name: "services-system-server-current.txt",
+    srcs: [
+        ":service-permission{.system-server.api.txt}",
+        ":non-updatable-system-server-current.txt",
+    ],
+    out: ["system-server-current.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "system-server-current.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system-server/api",
+            dest: "merge-android.txt",
+        },
+    ],
+}
+
+genrule {
+    name: "services-system-server-removed.txt",
+    srcs: [
+        ":service-permission{.system-server.removed-api.txt}",
+        ":non-updatable-system-server-removed.txt",
+    ],
+    out: ["system-server-removed.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "system-server-removed.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system-server/api",
+            dest: "merge-removed.txt",
+        },
+    ],
+}
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 260c8a4..f5bee6c 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -258,7 +258,7 @@
 
     public void runDisableAppDataIsolation() throws RemoteException {
         if (!SystemProperties.getBoolean(
-                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
             throw new IllegalStateException("Storage app data isolation is not enabled.");
         }
         final String pkgName = nextArg();
diff --git a/config/preloaded-classes b/config/preloaded-classes
index c6ec376..7c3fd8c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1844,11 +1844,9 @@
 android.ddm.DdmHandleAppName$Names
 android.ddm.DdmHandleAppName
 android.ddm.DdmHandleExit
-android.ddm.DdmHandleHeap
 android.ddm.DdmHandleHello
 android.ddm.DdmHandleNativeHeap
 android.ddm.DdmHandleProfiling
-android.ddm.DdmHandleThread
 android.ddm.DdmHandleViewDebug
 android.ddm.DdmRegister
 android.debug.AdbManager
diff --git a/core/api/current.txt b/core/api/current.txt
index ece1ce6..bca2e92 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -329,6 +329,7 @@
     field public static final int apiKey = 16843281; // 0x1010211
     field public static final int appCategory = 16844101; // 0x1010545
     field public static final int appComponentFactory = 16844154; // 0x101057a
+    field public static final int attributionTags = 16844353; // 0x1010641
     field public static final int author = 16843444; // 0x10102b4
     field public static final int authorities = 16842776; // 0x1010018
     field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -506,7 +507,7 @@
     field public static final int dashGap = 16843175; // 0x10101a7
     field public static final int dashWidth = 16843174; // 0x10101a6
     field public static final int data = 16842798; // 0x101002e
-    field public static final int dataExtractionRules = 16844350; // 0x101063e
+    field public static final int dataExtractionRules = 16844349; // 0x101063d
     field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
     field public static final int datePickerMode = 16843955; // 0x10104b3
     field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -528,8 +529,8 @@
     field public static final int detailSocialSummary = 16843428; // 0x10102a4
     field public static final int detailsElementBackground = 16843598; // 0x101034e
     field public static final int dial = 16843010; // 0x1010102
-    field public static final int dialTint = 16844342; // 0x1010636
-    field public static final int dialTintMode = 16844343; // 0x1010637
+    field public static final int dialTint = 16844341; // 0x1010635
+    field public static final int dialTintMode = 16844342; // 0x1010636
     field public static final int dialogCornerRadius = 16844145; // 0x1010571
     field public static final int dialogIcon = 16843252; // 0x10101f4
     field public static final int dialogLayout = 16843255; // 0x10101f7
@@ -727,14 +728,14 @@
     field public static final int groupIndicator = 16843019; // 0x101010b
     field public static final int gwpAsanMode = 16844310; // 0x1010616
     field public static final int hand_hour = 16843011; // 0x1010103
-    field public static final int hand_hourTint = 16844344; // 0x1010638
-    field public static final int hand_hourTintMode = 16844345; // 0x1010639
+    field public static final int hand_hourTint = 16844343; // 0x1010637
+    field public static final int hand_hourTintMode = 16844344; // 0x1010638
     field public static final int hand_minute = 16843012; // 0x1010104
-    field public static final int hand_minuteTint = 16844346; // 0x101063a
-    field public static final int hand_minuteTintMode = 16844347; // 0x101063b
+    field public static final int hand_minuteTint = 16844345; // 0x1010639
+    field public static final int hand_minuteTintMode = 16844346; // 0x101063a
     field public static final int hand_second = 16844323; // 0x1010623
-    field public static final int hand_secondTint = 16844348; // 0x101063c
-    field public static final int hand_secondTintMode = 16844349; // 0x101063d
+    field public static final int hand_secondTint = 16844347; // 0x101063b
+    field public static final int hand_secondTintMode = 16844348; // 0x101063c
     field public static final int handle = 16843354; // 0x101025a
     field public static final int handleProfiling = 16842786; // 0x1010022
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -818,7 +819,7 @@
     field public static final int installLocation = 16843447; // 0x10102b7
     field public static final int interactiveUiTimeout = 16844181; // 0x1010595
     field public static final int interpolator = 16843073; // 0x1010141
-    field public static final int isAccessibilityTool = 16844353; // 0x1010641
+    field public static final int isAccessibilityTool = 16844352; // 0x1010640
     field public static final int isAlwaysSyncable = 16843571; // 0x1010333
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
     field public static final int isAuxiliary = 16843647; // 0x101037f
@@ -970,8 +971,8 @@
     field public static final int maxLines = 16843091; // 0x1010153
     field public static final int maxLongVersionCode = 16844163; // 0x1010583
     field public static final int maxRecents = 16843846; // 0x1010446
-    field public static final int maxResizeHeight = 16844339; // 0x1010633
-    field public static final int maxResizeWidth = 16844338; // 0x1010632
+    field public static final int maxResizeHeight = 16844338; // 0x1010632
+    field public static final int maxResizeWidth = 16844337; // 0x1010631
     field public static final int maxRows = 16843059; // 0x1010133
     field public static final int maxSdkVersion = 16843377; // 0x1010271
     field public static final int maxWidth = 16843039; // 0x101011f
@@ -1074,7 +1075,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 passwordsActivity = 16844350; // 0x101063e
     field public static final int path = 16842794; // 0x101002a
     field public static final int pathAdvancedPattern = 16844320; // 0x1010620
     field public static final int pathData = 16843781; // 0x1010405
@@ -1199,7 +1200,6 @@
     field public static final int right = 16843183; // 0x10101af
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
-    field public static final int rippleStyle = 16844337; // 0x1010631
     field public static final int rollbackDataPolicy = 16844311; // 0x1010617
     field public static final int rotation = 16843558; // 0x1010326
     field public static final int rotationAnimation = 16844090; // 0x101053a
@@ -1261,7 +1261,7 @@
     field public static final int segmentedButtonStyle = 16843568; // 0x1010330
     field public static final int selectAllOnFocus = 16843102; // 0x101015e
     field public static final int selectable = 16843238; // 0x10101e6
-    field public static final int selectableAsDefault = 16844352; // 0x1010640
+    field public static final int selectableAsDefault = 16844351; // 0x101063f
     field public static final int selectableItemBackground = 16843534; // 0x101030e
     field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
     field @Deprecated public static final int selectedDateVerticalBar = 16843591; // 0x1010347
@@ -1394,6 +1394,7 @@
     field public static final int supportsRtl = 16843695; // 0x10103af
     field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
     field public static final int supportsUploading = 16843419; // 0x101029b
+    field public static final int suppressesSpellChecker = 16844354; // 0x1010642
     field public static final int switchMinWidth = 16843632; // 0x1010370
     field public static final int switchPadding = 16843633; // 0x1010371
     field public static final int switchPreferenceStyle = 16843629; // 0x101036d
@@ -1408,8 +1409,8 @@
     field public static final int tabWidgetStyle = 16842883; // 0x1010083
     field public static final int tag = 16842961; // 0x10100d1
     field public static final int targetActivity = 16843266; // 0x1010202
-    field public static final int targetCellHeight = 16844341; // 0x1010635
-    field public static final int targetCellWidth = 16844340; // 0x1010634
+    field public static final int targetCellHeight = 16844340; // 0x1010634
+    field public static final int targetCellWidth = 16844339; // 0x1010633
     field public static final int targetClass = 16842799; // 0x101002f
     field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
     field public static final int targetId = 16843740; // 0x10103dc
@@ -3026,6 +3027,7 @@
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
     field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
+    field public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; // 0xf
     field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
     field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
     field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
@@ -8491,7 +8493,7 @@
     field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
     field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
     field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
-    field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3; // 0x3
+    field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; // 0x4
     field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
     field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
     field public int autoAdvanceViewId;
@@ -9908,6 +9910,7 @@
     method public String getHtmlText();
     method public android.content.Intent getIntent();
     method public CharSequence getText();
+    method @Nullable public android.view.textclassifier.TextLinks getTextLinks();
     method public android.net.Uri getUri();
   }
 
@@ -9917,6 +9920,8 @@
     method public static boolean compareMimeTypes(String, String);
     method public int describeContents();
     method public String[] filterMimeTypes(String);
+    method public int getClassificationStatus();
+    method @FloatRange(from=0.0, to=1.0) public float getConfidenceScore(@NonNull String);
     method public android.os.PersistableBundle getExtras();
     method public CharSequence getLabel();
     method public String getMimeType(int);
@@ -9926,6 +9931,9 @@
     method public boolean isStyledText();
     method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CLASSIFICATION_COMPLETE = 3; // 0x3
+    field public static final int CLASSIFICATION_NOT_COMPLETE = 1; // 0x1
+    field public static final int CLASSIFICATION_NOT_PERFORMED = 2; // 0x2
     field @NonNull public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
     field public static final String MIMETYPE_TEXT_HTML = "text/html";
     field public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
@@ -10501,6 +10509,7 @@
     field public static final String DEVICE_POLICY_SERVICE = "device_policy";
     field public static final String DISPLAY_HASH_SERVICE = "display_hash";
     field public static final String DISPLAY_SERVICE = "display";
+    field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
     field public static final String DOWNLOAD_SERVICE = "download";
     field public static final String DROPBOX_SERVICE = "dropbox";
     field public static final String EUICC_SERVICE = "euicc";
@@ -10952,6 +10961,7 @@
     field public static final String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
     field public static final String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
+    field public static final String ACTION_MANAGE_UNUSED_APPS = "android.intent.action.MANAGE_UNUSED_APPS";
     field public static final String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
     field public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
     field public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -16687,13 +16697,9 @@
   public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
     ctor public RippleDrawable(@NonNull android.content.res.ColorStateList, @Nullable android.graphics.drawable.Drawable, @Nullable android.graphics.drawable.Drawable);
     method public int getRadius();
-    method public int getRippleStyle();
     method public void setColor(android.content.res.ColorStateList);
     method public void setRadius(int);
-    method public void setRippleStyle(int) throws java.lang.IllegalArgumentException;
     field public static final int RADIUS_AUTO = -1; // 0xffffffff
-    field public static final int STYLE_PATTERNED = 1; // 0x1
-    field public static final int STYLE_SOLID = 0; // 0x0
   }
 
   public class RotateDrawable extends android.graphics.drawable.DrawableWrapper {
@@ -20569,6 +20575,7 @@
     method @NonNull public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
     method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method @NonNull public android.media.AudioRecord.Builder setContext(@NonNull android.content.Context);
     method @NonNull public android.media.AudioRecord.Builder setPrivacySensitive(boolean);
   }
 
@@ -21892,6 +21899,7 @@
     method @NonNull public java.util.List<byte[]> getOfflineLicenseKeySetIds();
     method public int getOfflineLicenseState(@NonNull byte[]);
     method public int getOpenSessionCount();
+    method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
     method @NonNull public byte[] getPropertyByteArray(String);
     method @NonNull public String getPropertyString(@NonNull String);
     method @NonNull public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
@@ -22743,7 +22751,8 @@
   }
 
   public class MediaRecorder implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
-    ctor public MediaRecorder();
+    ctor @Deprecated public MediaRecorder();
+    ctor public MediaRecorder(@NonNull android.content.Context);
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
     method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
@@ -26140,10 +26149,6 @@
     ctor public NetworkSpecifier();
   }
 
-  public class ParseException extends java.lang.RuntimeException {
-    field public String response;
-  }
-
   public abstract class PlatformVpnProfile {
     method public final int getType();
     method @NonNull public final String getTypeString();
@@ -26555,236 +26560,236 @@
 
 package android.net.rtp {
 
-  public class AudioCodec {
-    method public static android.net.rtp.AudioCodec getCodec(int, String, String);
-    method public static android.net.rtp.AudioCodec[] getCodecs();
-    field public static final android.net.rtp.AudioCodec AMR;
-    field public static final android.net.rtp.AudioCodec GSM;
-    field public static final android.net.rtp.AudioCodec GSM_EFR;
-    field public static final android.net.rtp.AudioCodec PCMA;
-    field public static final android.net.rtp.AudioCodec PCMU;
-    field public final String fmtp;
-    field public final String rtpmap;
-    field public final int type;
+  @Deprecated public class AudioCodec {
+    method @Deprecated public static android.net.rtp.AudioCodec getCodec(int, String, String);
+    method @Deprecated public static android.net.rtp.AudioCodec[] getCodecs();
+    field @Deprecated public static final android.net.rtp.AudioCodec AMR;
+    field @Deprecated public static final android.net.rtp.AudioCodec GSM;
+    field @Deprecated public static final android.net.rtp.AudioCodec GSM_EFR;
+    field @Deprecated public static final android.net.rtp.AudioCodec PCMA;
+    field @Deprecated public static final android.net.rtp.AudioCodec PCMU;
+    field @Deprecated public final String fmtp;
+    field @Deprecated public final String rtpmap;
+    field @Deprecated public final int type;
   }
 
-  public class AudioGroup {
+  @Deprecated public class AudioGroup {
     ctor @Deprecated public AudioGroup();
-    ctor public AudioGroup(@NonNull android.content.Context);
-    method public void clear();
-    method public int getMode();
-    method public android.net.rtp.AudioStream[] getStreams();
-    method public void sendDtmf(int);
-    method public void setMode(int);
-    field public static final int MODE_ECHO_SUPPRESSION = 3; // 0x3
-    field public static final int MODE_MUTED = 1; // 0x1
-    field public static final int MODE_NORMAL = 2; // 0x2
-    field public static final int MODE_ON_HOLD = 0; // 0x0
+    ctor @Deprecated public AudioGroup(@NonNull android.content.Context);
+    method @Deprecated public void clear();
+    method @Deprecated public int getMode();
+    method @Deprecated public android.net.rtp.AudioStream[] getStreams();
+    method @Deprecated public void sendDtmf(int);
+    method @Deprecated public void setMode(int);
+    field @Deprecated public static final int MODE_ECHO_SUPPRESSION = 3; // 0x3
+    field @Deprecated public static final int MODE_MUTED = 1; // 0x1
+    field @Deprecated public static final int MODE_NORMAL = 2; // 0x2
+    field @Deprecated public static final int MODE_ON_HOLD = 0; // 0x0
   }
 
-  public class AudioStream extends android.net.rtp.RtpStream {
-    ctor public AudioStream(java.net.InetAddress) throws java.net.SocketException;
-    method public android.net.rtp.AudioCodec getCodec();
-    method public int getDtmfType();
-    method public android.net.rtp.AudioGroup getGroup();
-    method public final boolean isBusy();
-    method public void join(android.net.rtp.AudioGroup);
-    method public void setCodec(android.net.rtp.AudioCodec);
-    method public void setDtmfType(int);
+  @Deprecated public class AudioStream extends android.net.rtp.RtpStream {
+    ctor @Deprecated public AudioStream(java.net.InetAddress) throws java.net.SocketException;
+    method @Deprecated public android.net.rtp.AudioCodec getCodec();
+    method @Deprecated public int getDtmfType();
+    method @Deprecated public android.net.rtp.AudioGroup getGroup();
+    method @Deprecated public final boolean isBusy();
+    method @Deprecated public void join(android.net.rtp.AudioGroup);
+    method @Deprecated public void setCodec(android.net.rtp.AudioCodec);
+    method @Deprecated public void setDtmfType(int);
   }
 
-  public class RtpStream {
-    method public void associate(java.net.InetAddress, int);
-    method public java.net.InetAddress getLocalAddress();
-    method public int getLocalPort();
-    method public int getMode();
-    method public java.net.InetAddress getRemoteAddress();
-    method public int getRemotePort();
-    method public boolean isBusy();
-    method public void release();
-    method public void setMode(int);
-    field public static final int MODE_NORMAL = 0; // 0x0
-    field public static final int MODE_RECEIVE_ONLY = 2; // 0x2
-    field public static final int MODE_SEND_ONLY = 1; // 0x1
+  @Deprecated public class RtpStream {
+    method @Deprecated public void associate(java.net.InetAddress, int);
+    method @Deprecated public java.net.InetAddress getLocalAddress();
+    method @Deprecated public int getLocalPort();
+    method @Deprecated public int getMode();
+    method @Deprecated public java.net.InetAddress getRemoteAddress();
+    method @Deprecated public int getRemotePort();
+    method @Deprecated public boolean isBusy();
+    method @Deprecated public void release();
+    method @Deprecated public void setMode(int);
+    field @Deprecated public static final int MODE_NORMAL = 0; // 0x0
+    field @Deprecated public static final int MODE_RECEIVE_ONLY = 2; // 0x2
+    field @Deprecated public static final int MODE_SEND_ONLY = 1; // 0x1
   }
 
 }
 
 package android.net.sip {
 
-  public class SipAudioCall {
-    ctor public SipAudioCall(android.content.Context, android.net.sip.SipProfile);
-    method public void answerCall(int) throws android.net.sip.SipException;
-    method public void attachCall(android.net.sip.SipSession, String) throws android.net.sip.SipException;
-    method public void close();
-    method public void continueCall(int) throws android.net.sip.SipException;
-    method public void endCall() throws android.net.sip.SipException;
-    method public android.net.sip.SipProfile getLocalProfile();
-    method public android.net.sip.SipProfile getPeerProfile();
-    method public int getState();
-    method public void holdCall(int) throws android.net.sip.SipException;
-    method public boolean isInCall();
-    method public boolean isMuted();
-    method public boolean isOnHold();
-    method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
-    method public void sendDtmf(int);
-    method public void sendDtmf(int, android.os.Message);
-    method public void setListener(android.net.sip.SipAudioCall.Listener);
-    method public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
-    method public void setSpeakerMode(boolean);
-    method public void startAudio();
-    method public void toggleMute();
+  @Deprecated public class SipAudioCall {
+    ctor @Deprecated public SipAudioCall(android.content.Context, android.net.sip.SipProfile);
+    method @Deprecated public void answerCall(int) throws android.net.sip.SipException;
+    method @Deprecated public void attachCall(android.net.sip.SipSession, String) throws android.net.sip.SipException;
+    method @Deprecated public void close();
+    method @Deprecated public void continueCall(int) throws android.net.sip.SipException;
+    method @Deprecated public void endCall() throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipProfile getLocalProfile();
+    method @Deprecated public android.net.sip.SipProfile getPeerProfile();
+    method @Deprecated public int getState();
+    method @Deprecated public void holdCall(int) throws android.net.sip.SipException;
+    method @Deprecated public boolean isInCall();
+    method @Deprecated public boolean isMuted();
+    method @Deprecated public boolean isOnHold();
+    method @Deprecated public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
+    method @Deprecated public void sendDtmf(int);
+    method @Deprecated public void sendDtmf(int, android.os.Message);
+    method @Deprecated public void setListener(android.net.sip.SipAudioCall.Listener);
+    method @Deprecated public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
+    method @Deprecated public void setSpeakerMode(boolean);
+    method @Deprecated public void startAudio();
+    method @Deprecated public void toggleMute();
   }
 
-  public static class SipAudioCall.Listener {
-    ctor public SipAudioCall.Listener();
-    method public void onCallBusy(android.net.sip.SipAudioCall);
-    method public void onCallEnded(android.net.sip.SipAudioCall);
-    method public void onCallEstablished(android.net.sip.SipAudioCall);
-    method public void onCallHeld(android.net.sip.SipAudioCall);
-    method public void onCalling(android.net.sip.SipAudioCall);
-    method public void onChanged(android.net.sip.SipAudioCall);
-    method public void onError(android.net.sip.SipAudioCall, int, String);
-    method public void onReadyToCall(android.net.sip.SipAudioCall);
-    method public void onRinging(android.net.sip.SipAudioCall, android.net.sip.SipProfile);
-    method public void onRingingBack(android.net.sip.SipAudioCall);
+  @Deprecated public static class SipAudioCall.Listener {
+    ctor @Deprecated public SipAudioCall.Listener();
+    method @Deprecated public void onCallBusy(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCallEnded(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCallEstablished(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCallHeld(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCalling(android.net.sip.SipAudioCall);
+    method @Deprecated public void onChanged(android.net.sip.SipAudioCall);
+    method @Deprecated public void onError(android.net.sip.SipAudioCall, int, String);
+    method @Deprecated public void onReadyToCall(android.net.sip.SipAudioCall);
+    method @Deprecated public void onRinging(android.net.sip.SipAudioCall, android.net.sip.SipProfile);
+    method @Deprecated public void onRingingBack(android.net.sip.SipAudioCall);
   }
 
-  public class SipErrorCode {
-    method public static String toString(int);
-    field public static final int CLIENT_ERROR = -4; // 0xfffffffc
-    field public static final int CROSS_DOMAIN_AUTHENTICATION = -11; // 0xfffffff5
-    field public static final int DATA_CONNECTION_LOST = -10; // 0xfffffff6
-    field public static final int INVALID_CREDENTIALS = -8; // 0xfffffff8
-    field public static final int INVALID_REMOTE_URI = -6; // 0xfffffffa
-    field public static final int IN_PROGRESS = -9; // 0xfffffff7
-    field public static final int NO_ERROR = 0; // 0x0
-    field public static final int PEER_NOT_REACHABLE = -7; // 0xfffffff9
-    field public static final int SERVER_ERROR = -2; // 0xfffffffe
-    field public static final int SERVER_UNREACHABLE = -12; // 0xfffffff4
-    field public static final int SOCKET_ERROR = -1; // 0xffffffff
-    field public static final int TIME_OUT = -5; // 0xfffffffb
-    field public static final int TRANSACTION_TERMINTED = -3; // 0xfffffffd
+  @Deprecated public class SipErrorCode {
+    method @Deprecated public static String toString(int);
+    field @Deprecated public static final int CLIENT_ERROR = -4; // 0xfffffffc
+    field @Deprecated public static final int CROSS_DOMAIN_AUTHENTICATION = -11; // 0xfffffff5
+    field @Deprecated public static final int DATA_CONNECTION_LOST = -10; // 0xfffffff6
+    field @Deprecated public static final int INVALID_CREDENTIALS = -8; // 0xfffffff8
+    field @Deprecated public static final int INVALID_REMOTE_URI = -6; // 0xfffffffa
+    field @Deprecated public static final int IN_PROGRESS = -9; // 0xfffffff7
+    field @Deprecated public static final int NO_ERROR = 0; // 0x0
+    field @Deprecated public static final int PEER_NOT_REACHABLE = -7; // 0xfffffff9
+    field @Deprecated public static final int SERVER_ERROR = -2; // 0xfffffffe
+    field @Deprecated public static final int SERVER_UNREACHABLE = -12; // 0xfffffff4
+    field @Deprecated public static final int SOCKET_ERROR = -1; // 0xffffffff
+    field @Deprecated public static final int TIME_OUT = -5; // 0xfffffffb
+    field @Deprecated public static final int TRANSACTION_TERMINTED = -3; // 0xfffffffd
   }
 
-  public class SipException extends java.lang.Exception {
-    ctor public SipException();
-    ctor public SipException(String);
-    ctor public SipException(String, Throwable);
+  @Deprecated public class SipException extends java.lang.Exception {
+    ctor @Deprecated public SipException();
+    ctor @Deprecated public SipException(String);
+    ctor @Deprecated public SipException(String, Throwable);
   }
 
-  public class SipManager {
-    method public void close(String) throws android.net.sip.SipException;
-    method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
-    method public static String getCallId(android.content.Intent);
-    method public static String getOfferSessionDescription(android.content.Intent);
-    method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
-    method public static boolean isApiSupported(android.content.Context);
-    method public static boolean isIncomingCallIntent(android.content.Intent);
-    method public boolean isOpened(String) throws android.net.sip.SipException;
-    method public boolean isRegistered(String) throws android.net.sip.SipException;
-    method public static boolean isSipWifiOnly(android.content.Context);
-    method public static boolean isVoipSupported(android.content.Context);
-    method public android.net.sip.SipAudioCall makeAudioCall(android.net.sip.SipProfile, android.net.sip.SipProfile, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
-    method public android.net.sip.SipAudioCall makeAudioCall(String, String, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
-    method public static android.net.sip.SipManager newInstance(android.content.Context);
-    method public void open(android.net.sip.SipProfile) throws android.net.sip.SipException;
-    method public void open(android.net.sip.SipProfile, android.app.PendingIntent, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    method public void register(android.net.sip.SipProfile, int, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
-    method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    field public static final String EXTRA_CALL_ID = "android:sipCallID";
-    field public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
-    field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
+  @Deprecated public class SipManager {
+    method @Deprecated public void close(String) throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
+    method @Deprecated public static String getCallId(android.content.Intent);
+    method @Deprecated public static String getOfferSessionDescription(android.content.Intent);
+    method @Deprecated public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
+    method @Deprecated public static boolean isApiSupported(android.content.Context);
+    method @Deprecated public static boolean isIncomingCallIntent(android.content.Intent);
+    method @Deprecated public boolean isOpened(String) throws android.net.sip.SipException;
+    method @Deprecated public boolean isRegistered(String) throws android.net.sip.SipException;
+    method @Deprecated public static boolean isSipWifiOnly(android.content.Context);
+    method @Deprecated public static boolean isVoipSupported(android.content.Context);
+    method @Deprecated public android.net.sip.SipAudioCall makeAudioCall(android.net.sip.SipProfile, android.net.sip.SipProfile, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipAudioCall makeAudioCall(String, String, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
+    method @Deprecated public static android.net.sip.SipManager newInstance(android.content.Context);
+    method @Deprecated public void open(android.net.sip.SipProfile) throws android.net.sip.SipException;
+    method @Deprecated public void open(android.net.sip.SipProfile, android.app.PendingIntent, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    method @Deprecated public void register(android.net.sip.SipProfile, int, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    method @Deprecated public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
+    method @Deprecated public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    field @Deprecated public static final String EXTRA_CALL_ID = "android:sipCallID";
+    field @Deprecated public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
+    field @Deprecated public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
   }
 
-  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
-    method public int describeContents();
-    method public String getAuthUserName();
-    method public boolean getAutoRegistration();
-    method public String getDisplayName();
-    method public String getPassword();
-    method public int getPort();
-    method public String getProfileName();
-    method public String getProtocol();
-    method public String getProxyAddress();
-    method public boolean getSendKeepAlive();
-    method public String getSipDomain();
-    method public String getUriString();
-    method public String getUserName();
-    method public void setCallingUid(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR;
+  @Deprecated public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method @Deprecated public int describeContents();
+    method @Deprecated public String getAuthUserName();
+    method @Deprecated public boolean getAutoRegistration();
+    method @Deprecated public String getDisplayName();
+    method @Deprecated public String getPassword();
+    method @Deprecated public int getPort();
+    method @Deprecated public String getProfileName();
+    method @Deprecated public String getProtocol();
+    method @Deprecated public String getProxyAddress();
+    method @Deprecated public boolean getSendKeepAlive();
+    method @Deprecated public String getSipDomain();
+    method @Deprecated public String getUriString();
+    method @Deprecated public String getUserName();
+    method @Deprecated public void setCallingUid(int);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR;
   }
 
-  public static class SipProfile.Builder {
-    ctor public SipProfile.Builder(android.net.sip.SipProfile);
-    ctor public SipProfile.Builder(String) throws java.text.ParseException;
-    ctor public SipProfile.Builder(String, String) throws java.text.ParseException;
-    method public android.net.sip.SipProfile build();
-    method public android.net.sip.SipProfile.Builder setAuthUserName(String);
-    method public android.net.sip.SipProfile.Builder setAutoRegistration(boolean);
-    method public android.net.sip.SipProfile.Builder setDisplayName(String);
-    method public android.net.sip.SipProfile.Builder setOutboundProxy(String);
-    method public android.net.sip.SipProfile.Builder setPassword(String);
-    method public android.net.sip.SipProfile.Builder setPort(int) throws java.lang.IllegalArgumentException;
-    method public android.net.sip.SipProfile.Builder setProfileName(String);
-    method public android.net.sip.SipProfile.Builder setProtocol(String) throws java.lang.IllegalArgumentException;
-    method public android.net.sip.SipProfile.Builder setSendKeepAlive(boolean);
+  @Deprecated public static class SipProfile.Builder {
+    ctor @Deprecated public SipProfile.Builder(android.net.sip.SipProfile);
+    ctor @Deprecated public SipProfile.Builder(String) throws java.text.ParseException;
+    ctor @Deprecated public SipProfile.Builder(String, String) throws java.text.ParseException;
+    method @Deprecated public android.net.sip.SipProfile build();
+    method @Deprecated public android.net.sip.SipProfile.Builder setAuthUserName(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setAutoRegistration(boolean);
+    method @Deprecated public android.net.sip.SipProfile.Builder setDisplayName(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setOutboundProxy(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setPassword(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setPort(int) throws java.lang.IllegalArgumentException;
+    method @Deprecated public android.net.sip.SipProfile.Builder setProfileName(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setProtocol(String) throws java.lang.IllegalArgumentException;
+    method @Deprecated public android.net.sip.SipProfile.Builder setSendKeepAlive(boolean);
   }
 
-  public interface SipRegistrationListener {
-    method public void onRegistering(String);
-    method public void onRegistrationDone(String, long);
-    method public void onRegistrationFailed(String, int, String);
+  @Deprecated public interface SipRegistrationListener {
+    method @Deprecated public void onRegistering(String);
+    method @Deprecated public void onRegistrationDone(String, long);
+    method @Deprecated public void onRegistrationFailed(String, int, String);
   }
 
-  public final class SipSession {
-    method public void answerCall(String, int);
-    method public void changeCall(String, int);
-    method public void endCall();
-    method public String getCallId();
-    method public String getLocalIp();
-    method public android.net.sip.SipProfile getLocalProfile();
-    method public android.net.sip.SipProfile getPeerProfile();
-    method public int getState();
-    method public boolean isInCall();
-    method public void makeCall(android.net.sip.SipProfile, String, int);
-    method public void register(int);
-    method public void setListener(android.net.sip.SipSession.Listener);
-    method public void unregister();
+  @Deprecated public final class SipSession {
+    method @Deprecated public void answerCall(String, int);
+    method @Deprecated public void changeCall(String, int);
+    method @Deprecated public void endCall();
+    method @Deprecated public String getCallId();
+    method @Deprecated public String getLocalIp();
+    method @Deprecated public android.net.sip.SipProfile getLocalProfile();
+    method @Deprecated public android.net.sip.SipProfile getPeerProfile();
+    method @Deprecated public int getState();
+    method @Deprecated public boolean isInCall();
+    method @Deprecated public void makeCall(android.net.sip.SipProfile, String, int);
+    method @Deprecated public void register(int);
+    method @Deprecated public void setListener(android.net.sip.SipSession.Listener);
+    method @Deprecated public void unregister();
   }
 
-  public static class SipSession.Listener {
-    ctor public SipSession.Listener();
-    method public void onCallBusy(android.net.sip.SipSession);
-    method public void onCallChangeFailed(android.net.sip.SipSession, int, String);
-    method public void onCallEnded(android.net.sip.SipSession);
-    method public void onCallEstablished(android.net.sip.SipSession, String);
-    method public void onCalling(android.net.sip.SipSession);
-    method public void onError(android.net.sip.SipSession, int, String);
-    method public void onRegistering(android.net.sip.SipSession);
-    method public void onRegistrationDone(android.net.sip.SipSession, int);
-    method public void onRegistrationFailed(android.net.sip.SipSession, int, String);
-    method public void onRegistrationTimeout(android.net.sip.SipSession);
-    method public void onRinging(android.net.sip.SipSession, android.net.sip.SipProfile, String);
-    method public void onRingingBack(android.net.sip.SipSession);
+  @Deprecated public static class SipSession.Listener {
+    ctor @Deprecated public SipSession.Listener();
+    method @Deprecated public void onCallBusy(android.net.sip.SipSession);
+    method @Deprecated public void onCallChangeFailed(android.net.sip.SipSession, int, String);
+    method @Deprecated public void onCallEnded(android.net.sip.SipSession);
+    method @Deprecated public void onCallEstablished(android.net.sip.SipSession, String);
+    method @Deprecated public void onCalling(android.net.sip.SipSession);
+    method @Deprecated public void onError(android.net.sip.SipSession, int, String);
+    method @Deprecated public void onRegistering(android.net.sip.SipSession);
+    method @Deprecated public void onRegistrationDone(android.net.sip.SipSession, int);
+    method @Deprecated public void onRegistrationFailed(android.net.sip.SipSession, int, String);
+    method @Deprecated public void onRegistrationTimeout(android.net.sip.SipSession);
+    method @Deprecated public void onRinging(android.net.sip.SipSession, android.net.sip.SipProfile, String);
+    method @Deprecated public void onRingingBack(android.net.sip.SipSession);
   }
 
-  public static class SipSession.State {
-    method public static String toString(int);
-    field public static final int DEREGISTERING = 2; // 0x2
-    field public static final int INCOMING_CALL = 3; // 0x3
-    field public static final int INCOMING_CALL_ANSWERING = 4; // 0x4
-    field public static final int IN_CALL = 8; // 0x8
-    field public static final int NOT_DEFINED = 101; // 0x65
-    field public static final int OUTGOING_CALL = 5; // 0x5
-    field public static final int OUTGOING_CALL_CANCELING = 7; // 0x7
-    field public static final int OUTGOING_CALL_RING_BACK = 6; // 0x6
-    field public static final int PINGING = 9; // 0x9
-    field public static final int READY_TO_CALL = 0; // 0x0
-    field public static final int REGISTERING = 1; // 0x1
+  @Deprecated public static class SipSession.State {
+    method @Deprecated public static String toString(int);
+    field @Deprecated public static final int DEREGISTERING = 2; // 0x2
+    field @Deprecated public static final int INCOMING_CALL = 3; // 0x3
+    field @Deprecated public static final int INCOMING_CALL_ANSWERING = 4; // 0x4
+    field @Deprecated public static final int IN_CALL = 8; // 0x8
+    field @Deprecated public static final int NOT_DEFINED = 101; // 0x65
+    field @Deprecated public static final int OUTGOING_CALL = 5; // 0x5
+    field @Deprecated public static final int OUTGOING_CALL_CANCELING = 7; // 0x7
+    field @Deprecated public static final int OUTGOING_CALL_RING_BACK = 6; // 0x6
+    field @Deprecated public static final int PINGING = 9; // 0x9
+    field @Deprecated public static final int READY_TO_CALL = 0; // 0x0
+    field @Deprecated public static final int REGISTERING = 1; // 0x1
   }
 
 }
@@ -30430,6 +30435,7 @@
     field public static final String BASE_OS;
     field public static final String CODENAME;
     field public static final String INCREMENTAL;
+    field public static final int MEDIA_PERFORMANCE_CLASS;
     field public static final int PREVIEW_SDK_INT;
     field public static final String RELEASE;
     field @NonNull public static final String RELEASE_OR_CODENAME;
@@ -36867,11 +36873,12 @@
     method @NonNull public static android.content.Intent createInstallIntent();
     method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy);
     method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
-    method @NonNull public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
+    method @NonNull @WorkerThread public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
     method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
     method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String);
-    method public static boolean isCredentialManagementApp(@NonNull android.content.Context);
+    method @WorkerThread public static boolean isCredentialManagementApp(@NonNull android.content.Context);
     method public static boolean isKeyAlgorithmSupported(@NonNull String);
+    method @RequiresPermission(value="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP", conditional=true) @WorkerThread public static boolean removeCredentialManagementApp(@NonNull android.content.Context);
     field public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED";
     field public static final String ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED";
     field @Deprecated public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
@@ -39604,22 +39611,22 @@
     method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
     method public final void connectionServiceFocusReleased();
     method @Nullable public final android.telecom.RemoteConference createRemoteIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public final android.telecom.RemoteConnection createRemoteIncomingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method @Nullable public final android.telecom.RemoteConference createRemoteOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public final android.telecom.RemoteConnection createRemoteOutgoingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public final java.util.Collection<android.telecom.Conference> getAllConferences();
     method public final java.util.Collection<android.telecom.Connection> getAllConnections();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
-    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
-    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConferenceFailed(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -40536,6 +40543,9 @@
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
+    field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
+    field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
+    field public static final String KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL = "supports_sdp_negotiation_of_d2d_rtp_header_extensions_bool";
     field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
     field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool";
     field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
@@ -40614,14 +40624,6 @@
   public static final class CarrierConfigManager.Iwlan {
     field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
     field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
-    field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
-    field public static final int DH_GROUP_1536_BIT_MODP = 5; // 0x5
-    field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
-    field public static final int DH_GROUP_3072_BIT_MODP = 15; // 0xf
-    field public static final int DH_GROUP_4096_BIT_MODP = 16; // 0x10
-    field public static final int DH_GROUP_NONE = 0; // 0x0
-    field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
-    field public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13; // 0xd
     field public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; // 0x3
     field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
     field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
@@ -40629,12 +40631,6 @@
     field public static final int ID_TYPE_FQDN = 2; // 0x2
     field public static final int ID_TYPE_KEY_ID = 11; // 0xb
     field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
-    field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
-    field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
     field public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL = "iwlan.add_ke_to_child_session_rekey_bool";
     field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int";
     field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int";
@@ -40654,10 +40650,6 @@
     field public static final String KEY_IKE_REMOTE_ID_TYPE_INT = "iwlan.ike_remote_id_type_int";
     field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array";
     field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_ctr_key_size_int_array";
-    field public static final int KEY_LEN_AES_128 = 128; // 0x80
-    field public static final int KEY_LEN_AES_192 = 192; // 0xc0
-    field public static final int KEY_LEN_AES_256 = 256; // 0x100
-    field public static final int KEY_LEN_UNUSED = 0; // 0x0
     field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int";
     field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array";
     field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int";
@@ -40667,11 +40659,6 @@
     field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
     field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
     field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
-    field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
-    field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
-    field public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5; // 0x5
-    field public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6; // 0x6
-    field public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7; // 0x7
   }
 
   public abstract class CellIdentity implements android.os.Parcelable {
@@ -42040,6 +42027,7 @@
     method public static int getDefaultSmsSubscriptionId();
     method public static int getDefaultSubscriptionId();
     method public static int getDefaultVoiceSubscriptionId();
+    method public int getDeviceToDeviceStatusSharing(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
     method public static int getSlotIndex(int);
     method @Nullable public int[] getSubscriptionIds(int);
@@ -42052,6 +42040,7 @@
     method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
@@ -42063,6 +42052,11 @@
     field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
     field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
     field public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+    field public static final int D2D_SHARING_ALL = 3; // 0x3
+    field public static final int D2D_SHARING_ALL_CONTACTS = 1; // 0x1
+    field public static final int D2D_SHARING_DISABLED = 0; // 0x0
+    field public static final int D2D_SHARING_STARRED_CONTACTS = 2; // 0x2
+    field public static final String D2D_STATUS_SHARING = "d2d_sharing_status";
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
     field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
@@ -42912,7 +42906,7 @@
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled();
-    method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabledByUser() throws android.telephony.ims.ImsException;
+    method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabled() throws android.telephony.ims.ImsException;
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
@@ -49895,6 +49889,7 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
     method @Deprecated public android.view.Display getDefaultDisplay();
     method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
@@ -51476,7 +51471,6 @@
     method public int describeContents();
     method public void dump(android.util.Printer, String);
     method public android.content.ComponentName getComponent();
-    method public int getConfigChanges();
     method public String getId();
     method public int getIsDefaultResourceId();
     method public String getPackageName();
@@ -51487,6 +51481,7 @@
     method public int getSubtypeCount();
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
     method public CharSequence loadLabel(android.content.pm.PackageManager);
+    method public boolean suppressesSpellChecker();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
   }
@@ -51508,6 +51503,7 @@
     method public boolean isActive(android.view.View);
     method public boolean isActive();
     method public boolean isFullscreenMode();
+    method public boolean isInputMethodSuppressingSpellChecker();
     method @Deprecated public boolean isWatchingCursor(android.view.View);
     method public void restartInput(android.view.View);
     method public void sendAppPrivateCommand(android.view.View, String, android.os.Bundle);
@@ -51928,6 +51924,7 @@
     field public static final String TYPE_PHONE = "phone";
     field public static final String TYPE_UNKNOWN = "";
     field public static final String TYPE_URL = "url";
+    field public static final String WIDGET_TYPE_CLIPBOARD = "clipboard";
     field public static final String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
     field public static final String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
     field public static final String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
@@ -54983,6 +54980,7 @@
     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 addStableView(@IdRes int, @NonNull android.widget.RemoteViews, int);
     method public void addView(@IdRes int, android.widget.RemoteViews);
     method public android.view.View apply(android.content.Context, android.view.ViewGroup);
     method @Deprecated public android.widget.RemoteViews clone();
@@ -55041,6 +55039,7 @@
     method public void setRelativeScrollPosition(@IdRes int, int);
     method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
     method public void setRemoteAdapter(@IdRes int, android.content.Intent);
+    method public void setRemoteAdapter(@IdRes int, @NonNull android.widget.RemoteViews.RemoteCollectionItems);
     method public void setScrollPosition(@IdRes int, int);
     method public void setShort(@IdRes int, String, short);
     method public void setString(@IdRes int, String, String);
@@ -55080,6 +55079,25 @@
     ctor public RemoteViews.ActionException(String);
   }
 
+  public static final class RemoteViews.RemoteCollectionItems implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getItemCount();
+    method public long getItemId(int);
+    method @NonNull public android.widget.RemoteViews getItemView(int);
+    method public int getViewTypeCount();
+    method public boolean hasStableIds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews.RemoteCollectionItems> CREATOR;
+  }
+
+  public static final class RemoteViews.RemoteCollectionItems.Builder {
+    ctor public RemoteViews.RemoteCollectionItems.Builder();
+    method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder addItem(long, @NonNull android.widget.RemoteViews);
+    method @NonNull public android.widget.RemoteViews.RemoteCollectionItems build();
+    method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder setHasStableIds(boolean);
+    method @NonNull public android.widget.RemoteViews.RemoteCollectionItems.Builder setViewTypeCount(int);
+  }
+
   public static class RemoteViews.RemoteResponse {
     ctor public RemoteViews.RemoteResponse();
     method @NonNull public android.widget.RemoteViews.RemoteResponse addSharedElement(@IdRes int, @NonNull String);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 057e16c..a140e8a 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -55,10 +55,18 @@
 
 package android.content {
 
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+    method @NonNull public static android.net.Uri createContentUriAsUser(@NonNull android.net.Uri, @NonNull android.os.UserHandle);
+  }
+
   public abstract class Context {
     method @NonNull public android.os.UserHandle getUser();
   }
 
+  public class Intent implements java.lang.Cloneable android.os.Parcelable {
+    field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
+  }
+
 }
 
 package android.content.pm {
@@ -199,6 +207,16 @@
     method @Nullable public byte[] getWatchlistConfigHash();
   }
 
+  public class PacProxyManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void addPacProxyInstalledListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void removePacProxyInstalledListener(@NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setCurrentProxyScriptUrl(@Nullable android.net.ProxyInfo);
+  }
+
+  public static interface PacProxyManager.PacProxyInstalledListener {
+    method public void onPacProxyInstalled(@Nullable android.net.Network, @NonNull android.net.ProxyInfo);
+  }
+
   public final class Proxy {
     method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 70c945f..39e259d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -147,6 +147,7 @@
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
     field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
+    field public static final String MANAGE_SPEECH_RECOGNITION = "android.permission.MANAGE_SPEECH_RECOGNITION";
     field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS";
     field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
@@ -359,6 +360,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_systemTelevisionNotificationHandler = 17039409; // 0x1040031
     field public static final int config_systemWellbeing = 17039408; // 0x1040030
     field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
   }
@@ -1886,6 +1888,7 @@
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
@@ -2062,6 +2065,54 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
   }
 
+  public final class OobData implements android.os.Parcelable {
+    method @NonNull public static android.bluetooth.OobData.ClassicBuilder createClassicBuilder(@NonNull byte[], @NonNull byte[], @NonNull byte[]);
+    method @NonNull public static android.bluetooth.OobData.LeBuilder createLeBuilder(@NonNull byte[], @NonNull byte[], int);
+    method @NonNull public byte[] getClassOfDevice();
+    method @NonNull public byte[] getClassicLength();
+    method @NonNull public byte[] getConfirmationHash();
+    method @NonNull public byte[] getDeviceAddressWithType();
+    method @Nullable public byte[] getDeviceName();
+    method @Nullable public byte[] getLeAppearance();
+    method @NonNull public int getLeDeviceRole();
+    method @NonNull public int getLeFlags();
+    method @Nullable public byte[] getLeTemporaryKey();
+    method @NonNull public byte[] getRandomizerHash();
+    field public static final int CLASS_OF_DEVICE_OCTETS = 3; // 0x3
+    field public static final int CONFIRMATION_OCTETS = 16; // 0x10
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+    field public static final int DEVICE_ADDRESS_OCTETS = 7; // 0x7
+    field public static final int LE_APPEARANCE_OCTETS = 2; // 0x2
+    field public static final int LE_DEVICE_FLAG_OCTETS = 1; // 0x1
+    field public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 3; // 0x3
+    field public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 2; // 0x2
+    field public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 1; // 0x1
+    field public static final int LE_DEVICE_ROLE_OCTETS = 1; // 0x1
+    field public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0; // 0x0
+    field public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 2; // 0x2
+    field public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 1; // 0x1
+    field public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0; // 0x0
+    field public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 3; // 0x3
+    field public static final int LE_FLAG_SIMULTANEOUS_HOST = 4; // 0x4
+    field public static final int LE_TK_OCTETS = 16; // 0x10
+    field public static final int RANDOMIZER_OCTETS = 16; // 0x10
+  }
+
+  public static final class OobData.ClassicBuilder {
+    method @NonNull public android.bluetooth.OobData build();
+    method @NonNull public android.bluetooth.OobData.ClassicBuilder setClassOfDevice(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.ClassicBuilder setDeviceName(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.ClassicBuilder setRandomizerHash(@NonNull byte[]);
+  }
+
+  public static final class OobData.LeBuilder {
+    method @NonNull public android.bluetooth.OobData build();
+    method @NonNull public android.bluetooth.OobData.LeBuilder setDeviceName(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.LeBuilder setLeFlags(int);
+    method @NonNull public android.bluetooth.OobData.LeBuilder setLeTemporaryKey(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.LeBuilder setRandomizerHash(@NonNull byte[]);
+  }
+
 }
 
 package android.bluetooth.le {
@@ -2160,7 +2211,6 @@
     field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
-    field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String FONT_SERVICE = "font";
@@ -2238,6 +2288,7 @@
     field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE";
     field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
+    field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_HISTORY = "android.intent.action.REVIEW_PERMISSION_HISTORY";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
     field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
     field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
@@ -2881,11 +2932,16 @@
   }
 
   public static final class FontFamilyUpdateRequest.FontFamily {
-    ctor public FontFamilyUpdateRequest.FontFamily(@NonNull String, @NonNull java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font>);
     method @NonNull public java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font> getFonts();
     method @NonNull public String getName();
   }
 
+  public static final class FontFamilyUpdateRequest.FontFamily.Builder {
+    ctor public FontFamilyUpdateRequest.FontFamily.Builder(@NonNull String, @NonNull java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font>);
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.FontFamily.Builder addFont(@NonNull android.graphics.fonts.FontFamilyUpdateRequest.Font);
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.FontFamily build();
+  }
+
   public final class FontFileUpdateRequest {
     ctor public FontFileUpdateRequest(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[]);
     method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor();
@@ -2893,7 +2949,7 @@
   }
 
   public class FontManager {
-    method @NonNull public android.text.FontConfig getFontConfig();
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig();
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
     field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
@@ -3102,6 +3158,7 @@
     method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecEnabled();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecVersion();
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecVolumeControlEnabled();
     method public int getPhysicalAddress();
     method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
@@ -3109,6 +3166,8 @@
     method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
     method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
+    method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvSendStandbyOnSleep();
+    method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvWakeOnOneTouchPlay();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.String> getUserCecSettings();
     method public boolean isDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
     method public void powerOffDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
@@ -3117,10 +3176,13 @@
     method public void setActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecEnabled(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVersion(@NonNull int);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvSendStandbyOnSleep(@NonNull int);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvWakeOnOneTouchPlay(@NonNull int);
     field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
     field public static final int AVR_VOLUME_MUTED = 101; // 0x65
     field public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
@@ -3128,6 +3190,9 @@
     field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "send_standby_on_sleep";
     field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
     field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
+    field public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
+    field public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = "tv_wake_on_one_touch_play";
+    field public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE = "volume_control_enabled";
     field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
     field public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 160; // 0xa0
     field public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 161; // 0xa1
@@ -3223,6 +3288,12 @@
     field public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 11; // 0xb
     field public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 9; // 0x9
     field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
+    field public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0; // 0x0
+    field public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1; // 0x1
+    field public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED = 0; // 0x0
+    field public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED = 1; // 0x1
+    field public static final int VOLUME_CONTROL_DISABLED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_ENABLED = 1; // 0x1
   }
 
   public static interface HdmiControlManager.CecSettingChangeListener {
@@ -5123,6 +5194,21 @@
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
   }
 
+  public final class MediaRouter2 {
+    method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
+    method @Nullable public String getClientPackageName();
+    method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
+    method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
+    method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
+    method public void startScan();
+    method public void stopScan();
+    method public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
+  }
+
+  public abstract static class MediaRouter2.RouteCallback {
+    method public void onPreferredFeaturesChanged(@NonNull java.util.List<java.lang.String>);
+  }
+
   public class PlayerProxy {
     method public void pause();
     method public void setPan(float);
@@ -7462,22 +7548,22 @@
 
 package android.net.sip {
 
-  public class SipAudioCall {
-    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
-    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
+  @Deprecated public class SipAudioCall {
+    method @Deprecated @Nullable public android.net.rtp.AudioGroup getAudioGroup();
+    method @Deprecated public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
   }
 
-  public class SipManager {
-    method @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
-    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
-    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
-    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
-    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
-    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
+  @Deprecated public class SipManager {
+    method @Deprecated @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
+    field @Deprecated public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
+    field @Deprecated public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
+    field @Deprecated public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
+    field @Deprecated public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
+    field @Deprecated public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
   }
 
-  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
-    method public int getCallingUid();
+  @Deprecated public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method @Deprecated public int getCallingUid();
   }
 
 }
@@ -7498,12 +7584,12 @@
 package android.net.vcn {
 
   public class VcnManager {
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
-    method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
   }
 
-  public static interface VcnManager.VcnNetworkPolicyListener {
+  public static interface VcnManager.VcnNetworkPolicyChangeListener {
     method public void onPolicyChanged();
   }
 
@@ -8152,6 +8238,26 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.ParcelableHolder> CREATOR;
   }
 
+  public class PowerExemptionManager {
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull java.util.List<java.lang.String>);
+    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void addToTemporaryAllowList(@NonNull String, long, int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long addToTemporaryAllowListForEvent(@NonNull String, int, int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromAllowList(@NonNull String);
+    field public static final int EVENT_MMS = 2; // 0x2
+    field public static final int EVENT_SMS = 1; // 0x1
+    field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+    field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
+    field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+    field public static final int REASON_GEOFENCING = 100; // 0x64
+    field public static final int REASON_OTHER = 1; // 0x1
+    field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+    field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+    field public static final int REASON_UNKNOWN = 0; // 0x0
+    field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+    field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+  }
+
   public final class PowerManager {
     method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
     method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
@@ -8182,25 +8288,25 @@
     field public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1; // 0x1
   }
 
-  public class PowerWhitelistManager {
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
+  @Deprecated public class PowerWhitelistManager {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
-    field public static final int EVENT_MMS = 2; // 0x2
-    field public static final int EVENT_SMS = 1; // 0x1
-    field public static final int EVENT_UNSPECIFIED = 0; // 0x0
-    field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
-    field public static final int REASON_GEOFENCING = 100; // 0x64
-    field public static final int REASON_OTHER = 1; // 0x1
-    field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
-    field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
-    field public static final int REASON_UNKNOWN = 0; // 0x0
-    field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
-    field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
+    field @Deprecated public static final int EVENT_MMS = 2; // 0x2
+    field @Deprecated public static final int EVENT_SMS = 1; // 0x1
+    field @Deprecated public static final int EVENT_UNSPECIFIED = 0; // 0x0
+    field @Deprecated public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+    field @Deprecated public static final int REASON_GEOFENCING = 100; // 0x64
+    field @Deprecated public static final int REASON_OTHER = 1; // 0x1
+    field @Deprecated public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+    field @Deprecated public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+    field @Deprecated public static final int REASON_UNKNOWN = 0; // 0x0
+    field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+    field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
   }
 
   public class RecoverySystem {
@@ -9587,10 +9693,28 @@
 
 package android.service.displayhash {
 
+  public final class DisplayHashParams implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.util.Size getBufferSize();
+    method public boolean isBufferScaleWithFiltering();
+    method public boolean isGrayscaleBuffer();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
+  }
+
+  public static final class DisplayHashParams.Builder {
+    ctor public DisplayHashParams.Builder();
+    method @NonNull public android.service.displayhash.DisplayHashParams build();
+    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferScaleWithFiltering(boolean);
+    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
+    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
+  }
+
   public abstract class DisplayHasherService extends android.app.Service {
     ctor public DisplayHasherService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method @Nullable public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
+    method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
     method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
     field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
   }
@@ -9931,11 +10055,13 @@
 package android.service.rotationresolver {
 
   public final class RotationResolutionRequest implements android.os.Parcelable {
+    ctor public RotationResolutionRequest(@NonNull String, int, int, boolean, long);
     method public int describeContents();
     method public int getCurrentRotation();
     method @NonNull public String getPackageName();
     method public int getProposedRotation();
     method public long getTimeoutMillis();
+    method public boolean shouldUseCamera();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.service.rotationresolver.RotationResolutionRequest> CREATOR;
   }
@@ -10207,6 +10333,7 @@
     ctor public HotwordDetectionService();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public void onDetectFromDspSource(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback);
+    method public void onUpdateState(@Nullable android.os.Bundle, @Nullable android.os.SharedMemory);
     field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
   }
 
@@ -10217,10 +10344,8 @@
 
   public class VoiceInteractionService extends android.app.Service {
     method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.Bundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback);
     method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
-    method public final int setHotwordDetectionConfig(@Nullable android.os.Bundle);
-    field public static final int HOTWORD_CONFIG_FAILURE = 1; // 0x1
-    field public static final int HOTWORD_CONFIG_SUCCESS = 0; // 0x0
   }
 
 }
@@ -10320,6 +10445,7 @@
 
   public abstract class CallDiagnosticService extends android.app.Service {
     ctor public CallDiagnosticService();
+    method @NonNull public java.util.concurrent.Executor getExecutor();
     method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport);
     method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState);
@@ -10392,16 +10518,12 @@
     ctor public DiagnosticCall();
     method public final void clearDiagnosticMessage(int);
     method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
-    method @NonNull public android.telecom.Call.Details getCallDetails();
     method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
     method @Nullable public abstract CharSequence onCallDisconnected(int, int);
     method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
     method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
     method public abstract void onReceiveDeviceToDeviceMessage(int, int);
     method public final void sendDeviceToDeviceMessage(int, int);
-    field public static final int AUDIO_CODEC_AMR_NB = 3; // 0x3
-    field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2
-    field public static final int AUDIO_CODEC_EVS = 1; // 0x1
     field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
     field public static final int BATTERY_STATE_GOOD = 2; // 0x2
     field public static final int BATTERY_STATE_LOW = 1; // 0x1
@@ -10411,9 +10533,6 @@
     field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
     field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
     field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
-    field public static final int NETWORK_TYPE_IWLAN = 2; // 0x2
-    field public static final int NETWORK_TYPE_LTE = 1; // 0x1
-    field public static final int NETWORK_TYPE_NR = 3; // 0x3
   }
 
   public abstract class InCallService extends android.app.Service {
@@ -11435,7 +11554,7 @@
   }
 
   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>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(int, long);
   }
 
   public static interface TelephonyCallback.CallAttributesListener {
@@ -11578,7 +11697,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
-    method public boolean isNrDualConnectivityEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11664,6 +11783,7 @@
     field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
     field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
     field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+    field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
     field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
@@ -11919,7 +12039,7 @@
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
-    method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
+    method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
@@ -12014,7 +12134,6 @@
   }
 
   public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
-    method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel);
     method public int describeContents();
     method public long getGuaranteedDownlinkBitRate();
     method public long getGuaranteedUplinkBitRate();
@@ -12997,6 +13116,7 @@
     method public void onAutoConfigurationErrorReceived(int, @NonNull String);
     method public void onConfigurationChanged(@NonNull byte[]);
     method public void onConfigurationReset();
+    method public void onPreProvisioningReceived(@NonNull byte[]);
     method public void onRemoved();
   }
 
@@ -13466,6 +13586,7 @@
     method public int getConfigInt(int);
     method public String getConfigString(int);
     method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
+    method public final void notifyPreProvisioningReceived(@NonNull byte[]);
     method public final void notifyProvisionedValueChanged(int, int);
     method public final void notifyProvisionedValueChanged(int, String);
     method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
@@ -13797,6 +13918,7 @@
 package android.uwb {
 
   public final class AngleMeasurement implements android.os.Parcelable {
+    ctor public AngleMeasurement(double, double, double);
     method public int describeContents();
     method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
     method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
@@ -13805,14 +13927,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
   }
 
-  public static final class AngleMeasurement.Builder {
-    ctor public AngleMeasurement.Builder();
-    method @NonNull public android.uwb.AngleMeasurement build();
-    method @NonNull public android.uwb.AngleMeasurement.Builder setConfidenceLevel(double);
-    method @NonNull public android.uwb.AngleMeasurement.Builder setErrorRadians(double);
-    method @NonNull public android.uwb.AngleMeasurement.Builder setRadians(double);
-  }
-
   public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.uwb.AngleMeasurement getAltitude();
@@ -13822,10 +13936,9 @@
   }
 
   public static final class AngleOfArrivalMeasurement.Builder {
-    ctor public AngleOfArrivalMeasurement.Builder();
+    ctor public AngleOfArrivalMeasurement.Builder(@NonNull android.uwb.AngleMeasurement);
     method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
     method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
-    method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAzimuth(@NonNull android.uwb.AngleMeasurement);
   }
 
   public final class DistanceMeasurement implements android.os.Parcelable {
@@ -13925,7 +14038,7 @@
   public final class UwbManager {
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
     method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 86949e0..1587475 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -414,6 +414,7 @@
     method public long getLastNetworkLogRetrievalTime();
     method public long getLastSecurityLogRetrievalTime();
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
     method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
@@ -698,7 +699,8 @@
     field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
     field public static final String DREAM_SERVICE = "dream";
     field public static final String FONT_SERVICE = "font";
-    field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
+    field public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
+    field @Deprecated public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
@@ -706,6 +708,10 @@
     method public int getDisplayId();
   }
 
+  public class SyncAdapterType implements android.os.Parcelable {
+    method @Nullable public String getPackageName();
+  }
+
 }
 
 package android.content.integrity {
@@ -940,6 +946,12 @@
     method public void splitVertically(@NonNull android.graphics.Rect...);
   }
 
+  public class Typeface {
+    method @NonNull public static java.util.Map<java.lang.String,android.graphics.Typeface> deserializeFontMap(@NonNull java.nio.ByteBuffer) throws java.io.IOException;
+    method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
+    method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
+  }
+
 }
 
 package android.graphics.drawable {
@@ -962,7 +974,7 @@
 package android.graphics.fonts {
 
   public class FontManager {
-    method @NonNull public android.text.FontConfig getFontConfig();
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig();
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
     field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
@@ -1368,10 +1380,6 @@
     method public int getMaxMacroBlocks();
   }
 
-  public final class MediaDrm implements java.lang.AutoCloseable {
-    method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]);
-  }
-
   public final class MediaRoute2Info implements android.os.Parcelable {
     method @NonNull public String getOriginalId();
   }
@@ -1485,6 +1493,14 @@
 
 package android.os {
 
+  public final class BatteryStatsManager {
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void resetBattery(boolean);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryLevel(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setChargerAcOnline(boolean, boolean);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void suspendBatteryInput();
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void unplugBattery(boolean);
+  }
+
   public class Build {
     method public static boolean is64BitAbi(String);
     field public static final boolean IS_EMULATOR;
@@ -1930,8 +1946,7 @@
 package android.security {
 
   public final class KeyChain {
-    method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") public static boolean removeCredentialManagementApp(@NonNull android.content.Context);
-    method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") public static boolean setCredentialManagementApp(@NonNull android.content.Context, @NonNull String, @NonNull android.security.AppUriAuthenticationPolicy);
+    method @RequiresPermission("android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP") @WorkerThread public static boolean setCredentialManagementApp(@NonNull android.content.Context, @NonNull String, @NonNull android.security.AppUriAuthenticationPolicy);
   }
 
   public class KeyStoreException extends java.lang.Exception {
@@ -2125,6 +2140,14 @@
 
 }
 
+package android.speech {
+
+  public class SpeechRecognizer {
+    method public void setTemporaryOnDeviceRecognizer(@Nullable android.content.ComponentName);
+  }
+
+}
+
 package android.telecom {
 
   public static class Call.Details {
@@ -2530,6 +2553,7 @@
     method public default int getDisplayImePolicy(int);
     method public default void holdLock(android.os.IBinder, int);
     method public default void setDisplayImePolicy(int, int);
+    method public default void setForceCrossWindowBlurDisabled(boolean);
     method public default void setShouldShowSystemDecors(int, boolean);
     method public default void setShouldShowWithInsecureKeyguard(int, boolean);
     method public default boolean shouldShowSystemDecors(int);
@@ -2705,12 +2729,9 @@
     method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
   }
 
-  public final class InputMethodInfo implements android.os.Parcelable {
-    ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
-  }
-
   public final class InputMethodManager {
     method public int getDisplayId();
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
     method public boolean hasActiveInputConnection(@Nullable android.view.View);
     method public boolean isInputMethodPickerShown();
   }
@@ -2907,7 +2928,7 @@
     method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
-    method @BinderThread public void removeStartingWindow(int);
+    method @BinderThread public void removeStartingWindow(int, @Nullable android.view.SurfaceControl, @Nullable android.graphics.Rect, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
     method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer();
   }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index dab4a5d..d536821 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -555,6 +555,11 @@
      */
     public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
 
+    /**
+     * Action to dismiss the notification shade
+     */
+    public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15;
+
     private static final String LOG_TAG = "AccessibilityService";
 
     /**
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 233f737..a24f871 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -394,6 +394,20 @@
     }
 
     /**
+     * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+     * if the request orientation is not met, and will be shown in size-compat mode if the container
+     * size has changed.
+     * @hide
+     */
+    public static boolean supportsNonResizableMultiWindow() {
+        try {
+            return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
      * @hide
      */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0b59586..8977ba7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5775,11 +5775,14 @@
             // ResourcesImpl constructions.
             final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
 
-            if (diff == 0 && !shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
-                    && !movedToDifferentDisplay && mResourcesManager.isSameResourcesOverrideConfig(
-                            activityToken, amOverrideConfig)) {
-                // Nothing significant, don't proceed with updating and reporting.
-                return null;
+            if (diff == 0) {
+                if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
+                        && !movedToDifferentDisplay
+                        && mResourcesManager.isSameResourcesOverrideConfig(
+                                activityToken, amOverrideConfig)) {
+                    // Nothing significant, don't proceed with updating and reporting.
+                    return null;
+                }
             } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
                 // If this activity doesn't handle any of the config changes, then don't bother
                 // calling onConfigurationChanged. Otherwise, report to the activity for the
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dd1bc7c..d310e8f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -56,6 +56,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserManager;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -200,9 +201,12 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
     public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L;
 
+    private static final String FULL_LOG = "privacy_attribution_tag_full_log_enabled";
 
     private static final int MAX_UNFORWARDED_OPS = 10;
 
+    private static Boolean sFullLog = null;
+
     final Context mContext;
 
     @UnsupportedAppUsage
@@ -6972,6 +6976,26 @@
     AppOpsManager(Context context, IAppOpsService service) {
         mContext = context;
         mService = service;
+
+        if (mContext != null) {
+            final PackageManager pm = mContext.getPackageManager();
+            try {
+                if (pm != null && pm.checkPermission(Manifest.permission.READ_DEVICE_CONFIG,
+                        mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+                    DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+                            mContext.getMainExecutor(), properties -> {
+                                if (properties.getKeyset().contains(FULL_LOG)) {
+                                    sFullLog = properties.getBoolean(FULL_LOG, false);
+                                }
+                            });
+                    return;
+                }
+            } catch (Exception e) {
+                // This manager was made before DeviceConfig is ready, so it's a low-level
+                // system app. We likely don't care about its logs.
+            }
+        }
+        sFullLog = false;
     }
 
     /**
@@ -9110,10 +9134,20 @@
 
         StringBuilder sb = new StringBuilder();
         for (int i = firstInteresting; i <= lastInteresting; i++) {
+            if (sFullLog == null) {
+                try {
+                    sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                            FULL_LOG, false);
+                } catch (SecurityException e) {
+                    // This should not happen, but it may, in rare cases
+                    sFullLog = false;
+                }
+            }
+
             if (i != firstInteresting) {
                 sb.append('\n');
             }
-            if (sb.length() + trace[i].toString().length() > 600) {
+            if (!sFullLog && sb.length() + trace[i].toString().length() > 600) {
                 break;
             }
             sb.append(trace[i]);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3af0763..0358fe5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2586,9 +2586,7 @@
     @Override
     public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
         if (display == null) {
-            throw new UnsupportedOperationException("Token context can only be created from "
-                    + "other visual contexts, such as Activity or one created with "
-                    + "Context#createDisplayContext(Display)");
+            throw new IllegalArgumentException("Display must not be null");
         }
         final ContextImpl tokenContext = createBaseWindowContext(token, display);
         tokenContext.setResources(createWindowContextResources());
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 542f754..3bfddf7 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -289,6 +289,13 @@
     void setSplitScreenResizing(boolean resizing);
     boolean supportsLocalVoiceInteraction();
 
+    /**
+     * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+     * if the request orientation is not met, and will be shown in size-compat mode if the container
+     * size has changed.
+     */
+    boolean supportsNonResizableMultiWindow();
+
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2b45723..a3a8a5e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5011,9 +5011,13 @@
             boolean showProgress = handleProgressBar(contentView, ex, p);
             boolean hasSecondLine = showProgress;
             if (p.hasTitle()) {
-                contentView.setViewVisibility(R.id.title, View.VISIBLE);
-                contentView.setTextViewText(R.id.title, processTextSpans(p.title));
-                setTextViewColorPrimary(contentView, R.id.title, p);
+                contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
+                contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.title));
+                setTextViewColorPrimary(contentView, p.mTitleViewId, p);
+            } else if (p.mTitleViewId != R.id.title) {
+                // This alternate title view ID is not cleared by resetStandardTemplate
+                contentView.setViewVisibility(p.mTitleViewId, View.GONE);
+                contentView.setTextViewText(p.mTitleViewId, null);
             }
             if (p.text != null && p.text.length() != 0
                     && (!showProgress || p.mAllowTextWithProgress)) {
@@ -5307,7 +5311,7 @@
             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) {
+            if (!p.mReduceHighlights) {
                 textColor = getBackgroundColor(p);
                 pillColor = getAccentColor(p);
             }
@@ -5441,6 +5445,11 @@
                 // keep the divider visible between that title and the next text element.
                 return true;
             }
+            if (p.mHideAppName) {
+                // The app name is being hidden, so we definitely want to return here.
+                // Assume that there is a title which will replace it in the header.
+                return p.hasTitle();
+            }
             contentView.setViewVisibility(R.id.app_name_text, View.VISIBLE);
             contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
             contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
@@ -5817,7 +5826,7 @@
          *
          * @hide
          */
-        public RemoteViews makeNotificationHeader() {
+        public RemoteViews makeNotificationGroupHeader() {
             return makeNotificationHeader(mParams.reset()
                     .viewType(StandardTemplateParams.VIEW_TYPE_GROUP_HEADER)
                     .fillTextsFrom(this));
@@ -5943,7 +5952,7 @@
                     .viewType(StandardTemplateParams.VIEW_TYPE_PUBLIC)
                     .fillTextsFrom(this);
             if (isLowPriority) {
-                params.forceDefaultColor();
+                params.reduceHighlights();
             }
             view = makeNotificationHeader(params);
             view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
@@ -5966,7 +5975,7 @@
         public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
             StandardTemplateParams p = mParams.reset()
                     .viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
-                    .forceDefaultColor()
+                    .reduceHighlights()
                     .fillTextsFrom(this);
             if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
                 p.summaryText(createSummaryText());
@@ -6306,7 +6315,9 @@
          * @param p the template params to inflate this with
          */
         private @ColorInt int getRawColor(StandardTemplateParams p) {
-            if (p.forceDefaultColor) {
+            // When notifications are theme-tinted, the raw color is only used for the icon, so go
+            // ahead and keep that color instead of changing the color for minimized notifs.
+            if (p.mReduceHighlights && !mTintWithThemeAccent) {
                 return COLOR_DEFAULT;
             }
             return mN.color;
@@ -6583,10 +6594,6 @@
             return R.layout.notification_template_material_conversation;
         }
 
-        private int getCallLayoutResource() {
-            return R.layout.notification_template_material_call;
-        }
-
         private int getActionLayoutResource() {
             return R.layout.notification_material_action;
         }
@@ -9329,7 +9336,7 @@
          */
         @Override
         public RemoteViews makeContentView(boolean increasedHeight) {
-            return makeCallLayout();
+            return makeCallLayout(StandardTemplateParams.VIEW_TYPE_NORMAL);
         }
 
         /**
@@ -9337,14 +9344,14 @@
          */
         @Override
         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
-            return makeCallLayout();
+            return makeCallLayout(StandardTemplateParams.VIEW_TYPE_HEADS_UP);
         }
 
         /**
          * @hide
          */
         public RemoteViews makeBigContentView() {
-            return makeCallLayout();
+            return makeCallLayout(StandardTemplateParams.VIEW_TYPE_BIG);
         }
 
         @NonNull
@@ -9443,8 +9450,10 @@
             return resultActions;
         }
 
-        private RemoteViews makeCallLayout() {
+        private RemoteViews makeCallLayout(int viewType) {
+            final boolean isCollapsed = viewType == StandardTemplateParams.VIEW_TYPE_NORMAL;
             Bundle extras = mBuilder.mN.extras;
+            CharSequence title = mPerson != null ? mPerson.getName() : null;
             CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
             if (text == null) {
                 text = getDefaultText();
@@ -9452,20 +9461,30 @@
 
             // Bind standard template
             StandardTemplateParams p = mBuilder.mParams.reset()
-                    .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+                    .viewType(viewType)
                     .callStyleActions(true)
                     .allowTextWithProgress(true)
                     .hideLargeIcon(true)
+                    .hideAppName(isCollapsed)
+                    .titleViewId(R.id.conversation_text)
+                    .title(title)
                     .text(text)
                     .summaryText(mBuilder.processLegacyText(mVerificationText));
             mBuilder.mActions = getActionsListWithSystemActions();
-            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
-                    mBuilder.getCallLayoutResource(), p, null /* result */);
+            final RemoteViews contentView;
+            if (isCollapsed) {
+                contentView = mBuilder.applyStandardTemplate(
+                        R.layout.notification_template_material_call, p, null /* result */);
+            } else {
+                contentView = mBuilder.applyStandardTemplateWithActions(
+                        R.layout.notification_template_material_big_call, p, null /* result */);
+            }
 
             // Bind some extra conversation-specific header fields.
-            mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
-            mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
-            contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+            if (!p.mHideAppName) {
+                mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+                contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+            }
             bindCallerVerification(contentView, p);
 
             // Bind some custom CallLayout properties
@@ -12142,12 +12161,13 @@
         public static int VIEW_TYPE_NORMAL = 1;
         public static int VIEW_TYPE_BIG = 2;
         public static int VIEW_TYPE_HEADS_UP = 3;
-        public static int VIEW_TYPE_MINIMIZED = 4;
-        public static int VIEW_TYPE_PUBLIC = 5;
-        public static int VIEW_TYPE_GROUP_HEADER = 6;
+        public static int VIEW_TYPE_MINIMIZED = 4;    // header only for minimized state
+        public static int VIEW_TYPE_PUBLIC = 5;       // header only for automatic public version
+        public static int VIEW_TYPE_GROUP_HEADER = 6; // header only for top of group
 
         int mViewType = VIEW_TYPE_UNSPECIFIED;
         boolean mHeaderless;
+        boolean mHideAppName;
         boolean mHideTitle;
         boolean mHideActions;
         boolean mHideProgress;
@@ -12155,6 +12175,7 @@
         boolean mPromotePicture;
         boolean mCallStyleActions;
         boolean mAllowTextWithProgress;
+        int mTitleViewId;
         int mTextViewId;
         CharSequence title;
         CharSequence text;
@@ -12163,11 +12184,12 @@
         int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
         boolean hideLargeIcon;
         boolean allowColorization  = true;
-        boolean forceDefaultColor = false;
+        boolean mReduceHighlights = false;
 
         final StandardTemplateParams reset() {
             mViewType = VIEW_TYPE_UNSPECIFIED;
             mHeaderless = false;
+            mHideAppName = false;
             mHideTitle = false;
             mHideActions = false;
             mHideProgress = false;
@@ -12175,6 +12197,7 @@
             mPromotePicture = false;
             mCallStyleActions = false;
             mAllowTextWithProgress = false;
+            mTitleViewId = R.id.title;
             mTextViewId = R.id.text;
             title = null;
             text = null;
@@ -12182,7 +12205,7 @@
             headerTextSecondary = null;
             maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
             allowColorization = true;
-            forceDefaultColor = false;
+            mReduceHighlights = false;
             return this;
         }
 
@@ -12200,6 +12223,11 @@
             return this;
         }
 
+        public StandardTemplateParams hideAppName(boolean hideAppName) {
+            mHideAppName = hideAppName;
+            return this;
+        }
+
         final StandardTemplateParams hideActions(boolean hideActions) {
             this.mHideActions = hideActions;
             return this;
@@ -12235,6 +12263,11 @@
             return this;
         }
 
+        public StandardTemplateParams titleViewId(int titleViewId) {
+            mTitleViewId = titleViewId;
+            return this;
+        }
+
         public StandardTemplateParams textViewId(int textViewId) {
             mTextViewId = textViewId;
             return this;
@@ -12270,8 +12303,8 @@
             return this;
         }
 
-        final StandardTemplateParams forceDefaultColor() {
-            this.forceDefaultColor = true;
+        final StandardTemplateParams reduceHighlights() {
+            this.mReduceHighlights = true;
             return this;
         }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 43c14a9..31b0d41 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -129,11 +129,13 @@
 import android.net.IEthernetManager;
 import android.net.IIpSecService;
 import android.net.INetworkPolicyManager;
+import android.net.IPacProxyManager;
 import android.net.IVpnManager;
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
 import android.net.NetworkWatchlistManager;
+import android.net.PacProxyManager;
 import android.net.TetheringManager;
 import android.net.VpnManager;
 import android.net.lowpan.ILowpanManager;
@@ -374,6 +376,15 @@
         // (which extends it).
         SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
 
+        registerService(Context.PAC_PROXY_SERVICE, PacProxyManager.class,
+                new CachedServiceFetcher<PacProxyManager>() {
+            @Override
+            public PacProxyManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getServiceOrThrow(Context.PAC_PROXY_SERVICE);
+                IPacProxyManager service = IPacProxyManager.Stub.asInterface(b);
+                return new PacProxyManager(ctx.getOuterContext(), service);
+            }});
+
         registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
             @Override
             public IBinder createService() throws ServiceNotFoundException {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8e53b5b..7dbbc54 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -57,6 +57,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadSystemException;
+import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -120,6 +121,8 @@
     /** {@hide} */
     private static final String VALUE_CMF_COLOR =
             android.os.SystemProperties.get("ro.boot.hardware.color");
+    /** {@hide} */
+    private static final String WALLPAPER_CMF_PATH = "/wallpaper/image/";
 
     /**
      * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
@@ -2066,22 +2069,17 @@
             return null;
         } else {
             whichProp = PROP_WALLPAPER;
-            final int defaultColorResId = context.getResources().getIdentifier(
-                    "default_wallpaper_" + VALUE_CMF_COLOR, "drawable", "android");
-            defaultResId =
-                    defaultColorResId == 0 ? com.android.internal.R.drawable.default_wallpaper
-                            : defaultColorResId;
+            defaultResId = com.android.internal.R.drawable.default_wallpaper;
         }
         final String path = SystemProperties.get(whichProp);
-        if (!TextUtils.isEmpty(path)) {
-            final File file = new File(path);
-            if (file.exists()) {
-                try {
-                    return new FileInputStream(file);
-                } catch (IOException e) {
-                    // Ignored, fall back to platform default below
-                }
-            }
+        final InputStream wallpaperInputStream = getWallpaperInputStream(path);
+        if (wallpaperInputStream != null) {
+            return wallpaperInputStream;
+        }
+        final String cmfPath = getCmfWallpaperPath();
+        final InputStream cmfWallpaperInputStream = getWallpaperInputStream(cmfPath);
+        if (cmfWallpaperInputStream != null) {
+            return cmfWallpaperInputStream;
         }
         try {
             return context.getResources().openRawResource(defaultResId);
@@ -2091,6 +2089,25 @@
         return null;
     }
 
+    private static InputStream getWallpaperInputStream(String path) {
+        if (!TextUtils.isEmpty(path)) {
+            final File file = new File(path);
+            if (file.exists()) {
+                try {
+                    return new FileInputStream(file);
+                } catch (IOException e) {
+                    // Ignored, fall back to platform default
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String getCmfWallpaperPath() {
+        return Environment.getProductDirectory() + WALLPAPER_CMF_PATH + "default_wallpaper_"
+                + VALUE_CMF_COLOR;
+    }
+
     /**
      * Return {@link ComponentName} of the default live wallpaper, or
      * {@code null} if none is defined.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ccf41e5..930717b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -987,7 +987,8 @@
      * The default for this extra is {@code false} - by default, the admin of a fully-managed
      * device has the ability to grant sensors-related permissions.
      *
-     * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only.
+     * <p>Use only for device owner provisioning.
+     * @see #ACTION_GET_PROVISIONING_MODE
      */
     public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT =
             "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
@@ -11838,7 +11839,7 @@
 
     /**
      * @hide
-     * Force update user setup completed status. This API has no effect on user build.
+     * Force update user setup completed status.
      * @throws {@link SecurityException} if the caller has no
      *         {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
      *         not {@link UserHandle#SYSTEM_USER}
@@ -13725,4 +13726,22 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Lists apps that are exempt from policies (such as
+     * {@link #setPackagesSuspended(ComponentName, String[], boolean)}).
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_DEVICE_ADMINS)
+    public @NonNull Set<String> getPolicyExemptApps() {
+        if (mService == null) return Collections.emptySet();
+
+        try {
+            return new HashSet<>(mService.listPolicyExemptApps());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 25ca599..e98720c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -177,6 +177,7 @@
 
     String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended);
     boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName);
+    List<String> listPolicyExemptApps();
 
     boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
     void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 6ac1c1a..1cbb2fb 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -121,7 +121,7 @@
      *
      * @see #widgetFeatures
      */
-    public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3;
+    public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ec94faa..07dbdce 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1311,7 +1311,6 @@
      * the bonding process completes, and its result.
      * <p>Android system services will handle the necessary user interactions
      * to confirm and complete the bonding process.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
      * @param transport The transport to use for the pairing procedure.
      * @return false on immediate error, true if bonding will begin
@@ -1319,8 +1318,9 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean createBond(int transport) {
-        return createBondOutOfBand(transport, null);
+        return createBondInternal(transport, null, null);
     }
 
     /**
@@ -1334,22 +1334,39 @@
      * <p>Android system services will handle the necessary user interactions
      * to confirm and complete the bonding process.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     * <p>There are two possible versions of OOB Data.  This data can come in as
+     * P192 or P256.  This is a reference to the cryptography used to generate the key.
+     * The caller may pass one or both.  If both types of data are passed, then the
+     * P256 data will be preferred, and thus used.
      *
      * @param transport - Transport to use
-     * @param oobData - Out Of Band data
+     * @param remoteP192Data - Out Of Band data (P192) or null
+     * @param remoteP256Data - Out Of Band data (P256) or null
      * @return false on immediate error, true if bonding will begin
      * @hide
      */
-    public boolean createBondOutOfBand(int transport, OobData oobData) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
+            @Nullable OobData remoteP256Data) {
+        if (remoteP192Data == null && remoteP256Data == null) {
+            throw new IllegalArgumentException(
+                "One or both arguments for the OOB data types are required to not be null."
+                + "  Please use createBond() instead if you do not have OOB data to pass.");
+        }
+        return createBondInternal(transport, remoteP192Data, remoteP256Data);
+    }
+
+    private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
+            @Nullable OobData remoteP256Data) {
         final IBluetooth service = sService;
         if (service == null) {
             Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
             return false;
         }
         try {
-            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-            return service.createBond(this, transport, oobData, adapter.getOpPackageName());
+            return service.createBond(this, transport, remoteP192Data, remoteP256Data,
+                    BluetoothAdapter.getDefaultAdapter().getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
@@ -1380,27 +1397,6 @@
     }
 
     /**
-     * Set the Out Of Band data for a remote device to be used later
-     * in the pairing mechanism. Users can obtain this data through other
-     * trusted channels
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
-     *
-     * @param hash Simple Secure pairing hash
-     * @param randomizer The random key obtained using OOB
-     * @return false on error; true otherwise
-     * @hide
-     */
-    public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
-        //TODO(BT)
-      /*
-      try {
-        return sService.setDeviceOutOfBandData(this, hash, randomizer);
-      } catch (RemoteException e) {Log.e(TAG, "", e);} */
-        return false;
-    }
-
-    /**
      * Cancel an in-progress bonding request started with {@link #createBond}.
      *
      * @return true on success, false on error
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 0d0c6ab..9810746 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,88 +16,950 @@
 
 package android.bluetooth;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
+import java.lang.IllegalArgumentException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Out Of Band Data for Bluetooth device pairing.
  *
  * <p>This object represents optional data obtained from a remote device through
- * an out-of-band channel (eg. NFC).
+ * an out-of-band channel (eg. NFC, QR).
+ *
+ * <p>References:
+ * NFC AD Forum SSP 1.1 (AD)
+ * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf}
+ * Core Specification Supplement (CSS) V9
+ *
+ * <p>There are several BR/EDR Examples
+ *
+ * <p>Negotiated Handover:
+ *   Bluetooth Carrier Configuration Record:
+ *    - OOB Data Length
+ *    - Device Address
+ *    - Class of Device
+ *    - Simple Pairing Hash C
+ *    - Simple Pairing Randomizer R
+ *    - Service Class UUID
+ *    - Bluetooth Local Name
+ *
+ * <p>Static Handover:
+ *   Bluetooth Carrier Configuration Record:
+ *    - OOB Data Length
+ *    - Device Address
+ *    - Class of Device
+ *    - Service Class UUID
+ *    - Bluetooth Local Name
+ *
+ * <p>Simplified Tag Format for Single BT Carrier:
+ *   Bluetooth OOB Data Record:
+ *    - OOB Data Length
+ *    - Device Address
+ *    - Class of Device
+ *    - Service Class UUID
+ *    - Bluetooth Local Name
  *
  * @hide
  */
-public class OobData implements Parcelable {
-    private byte[] mLeBluetoothDeviceAddress;
-    private byte[] mSecurityManagerTk;
-    private byte[] mLeSecureConnectionsConfirmation;
-    private byte[] mLeSecureConnectionsRandom;
+@SystemApi
+public final class OobData implements Parcelable {
 
-    public byte[] getLeBluetoothDeviceAddress() {
-        return mLeBluetoothDeviceAddress;
+    private static final String TAG = "OobData";
+    /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
+    @SystemApi
+    private static final int OOB_LENGTH_OCTETS = 2;
+    /**
+     * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1).
+     * (AD 3.1.2) (CSS 1.6.2)
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_ADDRESS_OCTETS = 7;
+    /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int CLASS_OF_DEVICE_OCTETS = 3;
+    /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int CONFIRMATION_OCTETS = 16;
+    /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int RANDOMIZER_OCTETS = 16;
+    /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_OCTETS = 1;
+    /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
+    @SystemApi
+    public static final int LE_TK_OCTETS = 16;
+    /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
+    @SystemApi
+    public static final int LE_APPEARANCE_OCTETS = 2;
+    /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
+    @SystemApi
+    public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
+
+    // Le Roles
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "LE_DEVICE_ROLE_" },
+        value = {
+            LE_DEVICE_ROLE_PERIPHERAL_ONLY,
+            LE_DEVICE_ROLE_CENTRAL_ONLY,
+            LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
+            LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
+        }
+    )
+    public @interface LeRole {}
+
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
+
+    // Le Flags
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "LE_FLAG_" },
+        value = {
+            LE_FLAG_LIMITED_DISCOVERY_MODE,
+            LE_FLAG_GENERAL_DISCOVERY_MODE,
+            LE_FLAG_BREDR_NOT_SUPPORTED,
+            LE_FLAG_SIMULTANEOUS_CONTROLLER,
+            LE_FLAG_SIMULTANEOUS_HOST
+        }
+    )
+    public @interface LeFlag {}
+
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
+
+    /**
+     * Main creation method for creating a Classic version of {@link OobData}.
+     *
+     * <p>This object will allow the caller to call {@link ClassicBuilder#build()}
+     * to build the data object or add any option information to the builder.
+     *
+     * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+     * of data. Data is derived from controller/host stack and is required for pairing OOB.
+     * @param classicLength byte array representing the length of data from 8-65535 across 2
+     * octets (0xXXXX).
+     * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+     * that owns the OOB data. (i.e. the originator) [6 octets]
+     *
+     * @return a Classic Builder instance with all the given data set or null.
+     *
+     * @throws IllegalArgumentException if any of the values fail to be set.
+     * @throws NullPointerException if any argument is null.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash,
+            @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) {
+        return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType);
     }
 
     /**
-     * Sets the LE Bluetooth Device Address value to be used during LE pairing.
-     * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for
-     * a detailed description.
+     * Main creation method for creating a LE version of {@link OobData}.
+     *
+     * <p>This object will allow the caller to call {@link LeBuilder#build()}
+     * to build the data object or add any option information to the builder.
+     *
+     * @param deviceAddressWithType the LE device address plus the address type (7 octets);
+     * not null.
+     * @param leDeviceRole whether the device supports Peripheral, Central,
+     * Both including preference; not null. (1 octet)
+     * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+     * of data. Data is derived from controller/host stack and is
+     * required for pairing OOB.
+     *
+     * <p>Possible LE Device Role Values:
+     * 0x00 Only Peripheral supported
+     * 0x01 Only Central supported
+     * 0x02 Central & Peripheral supported; Peripheral Preferred
+     * 0x03 Only peripheral supported; Central Preferred
+     * 0x04 - 0xFF Reserved
+     *
+     * @return a LeBuilder instance with all the given data set or null.
+     *
+     * @throws IllegalArgumentException if any of the values fail to be set.
+     * @throws NullPointerException if any argument is null.
+     *
+     * @hide
      */
-    public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) {
-        mLeBluetoothDeviceAddress = leBluetoothDeviceAddress;
-    }
-
-    public byte[] getSecurityManagerTk() {
-        return mSecurityManagerTk;
+    @NonNull
+    @SystemApi
+    public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash,
+            @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) {
+        return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole);
     }
 
     /**
-     * Sets the Temporary Key value to be used by the LE Security Manager during
-     * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6,
-     * Part A 1.8 for a detailed description.
+     * Builds an {@link OobData} object and validates that the required combination
+     * of values are present to create the LE specific OobData type.
+     *
+     * @hide
      */
-    public void setSecurityManagerTk(byte[] securityManagerTk) {
-        mSecurityManagerTk = securityManagerTk;
+    @SystemApi
+    public static final class LeBuilder {
+
+        /**
+         * It is recommended that this Hash C is generated anew for each
+         * pairing.
+         *
+         * <p>It should be noted that on passive NFC this isn't possible as the data is static
+         * and immutable.
+         */
+        private byte[] mConfirmationHash = null;
+
+        /**
+         * Optional, but adds more validity to the pairing.
+         *
+         * <p>If not present a value of 0 is assumed.
+         */
+        private byte[] mRandomizerHash = new byte[] {
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        };
+
+        /**
+         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+         *
+         * <p>This is the name that may be displayed to the device user as part of the UI.
+         */
+        private byte[] mDeviceName = null;
+
+        /**
+         * Sets the Bluetooth Device name to be used for UI purposes.
+         *
+         * <p>Optional attribute.
+         *
+         * @param deviceName byte array representing the name, may be 0 in length, not null.
+         *
+         * @return {@link OobData#ClassicBuilder}
+         *
+         * @throws NullPointerException if deviceName is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
+            Preconditions.checkNotNull(deviceName);
+            this.mDeviceName = deviceName;
+            return this;
+        }
+
+        /**
+         * The Bluetooth Device Address is the address to which the OOB data belongs.
+         *
+         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+         *
+         * <p> Address is encoded in Little Endian order.
+         *
+         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+         */
+        private final byte[] mDeviceAddressWithType;
+
+        /**
+         * During an LE connection establishment, one must be in the Peripheral mode and the other
+         * in the Central role.
+         *
+         * <p>Possible Values:
+         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+         * Peripheral Preferred
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+         * 0x04 - 0xFF Reserved
+         */
+        private final @LeRole int mLeDeviceRole;
+
+        /**
+         * Temporary key value from the Security Manager.
+         *
+         * <p> Must be {@link LE_TK_OCTETS} in size
+         */
+        private byte[] mLeTemporaryKey = null;
+
+        /**
+         * Defines the representation of the external appearance of the device.
+         *
+         * <p>For example, a mouse, remote control, or keyboard.
+         *
+         * <p>Used for visual on discovering device to represent icon/string/etc...
+         */
+        private byte[] mLeAppearance = null;
+
+        /**
+         * Contains which discoverable mode to use, BR/EDR support and capability.
+         *
+         * <p>Possible LE Flags:
+         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+         * LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Controller).
+         * Bit 49 of LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Host).
+         * Bit 55 of LMP Feature Mask Definitions.
+         * <b>0x05- 0x07 Reserved</b>
+         */
+        private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
+
+        /**
+         * Constructing an OobData object for use with LE requires
+         * a LE Device Address and LE Device Role as well as the Confirmation
+         * and optionally, the Randomizer, however it is recommended to use.
+         *
+         * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+         * octets of data. Data is derived from controller/host stack and is required for
+         * pairing OOB.
+         * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte
+         * address type.
+         * @param leDeviceRole indicating device's role and preferences (Central or Peripheral)
+         *
+         * <p>Possible Values:
+         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+         * Peripheral Preferred
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+         * 0x04 - 0xFF Reserved
+         *
+         * @throws IllegalArgumentException if deviceAddressWithType is not
+         *                                  {@link LE_DEVICE_ADDRESS_OCTETS} octets
+         * @throws NullPointerException if any argument is null.
+         */
+        private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType,
+                @LeRole int leDeviceRole) {
+            Preconditions.checkNotNull(confirmationHash);
+            Preconditions.checkNotNull(deviceAddressWithType);
+            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+                throw new IllegalArgumentException("confirmationHash must be "
+                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
+            }
+            this.mConfirmationHash = confirmationHash;
+            if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
+                throw new IllegalArgumentException("confirmationHash must be "
+                    + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length.");
+            }
+            this.mDeviceAddressWithType = deviceAddressWithType;
+            if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
+                    || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
+                throw new IllegalArgumentException("leDeviceRole must be a valid value.");
+            }
+            this.mLeDeviceRole = leDeviceRole;
+        }
+
+        /**
+         * Sets the Temporary Key value to be used by the LE Security Manager during
+         * LE pairing.
+         *
+         * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6,
+         * Part A 1.8 for a detailed description.
+         *
+         * @return {@link OobData#Builder}
+         *
+         * @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
+         * @throws NullinterException if leTemporaryKey is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
+            Preconditions.checkNotNull(leTemporaryKey);
+            if (leTemporaryKey.length != LE_TK_OCTETS) {
+                throw new IllegalArgumentException("leTemporaryKey must be "
+                        + LE_TK_OCTETS + " octets in length.");
+            }
+            this.mLeTemporaryKey = leTemporaryKey;
+            return this;
+        }
+
+        /**
+         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+         * of data. Data is derived from controller/host stack and is required for pairing OOB.
+         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+         *
+         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+         * @throws NullPointerException if randomizerHash is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+            Preconditions.checkNotNull(randomizerHash);
+            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+                throw new IllegalArgumentException("randomizerHash must be "
+                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
+            }
+            this.mRandomizerHash = randomizerHash;
+            return this;
+        }
+
+        /**
+         * Sets the LE Flags necessary for the pairing scenario or discovery mode.
+         *
+         * @param leFlags enum value representing the 1 octet of data about discovery modes.
+         *
+         * <p>Possible LE Flags:
+         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+         * LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Host).
+         * Bit 55 of LMP Feature Mask Definitions.
+         * 0x05- 0x07 Reserved
+         *
+         * @throws IllegalArgumentException for invalid flag
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setLeFlags(@LeFlag int leFlags) {
+            if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
+                throw new IllegalArgumentException("leFlags must be a valid value.");
+            }
+            this.mLeFlags = leFlags;
+            return this;
+        }
+
+        /**
+         * Validates and builds the {@link OobData} object for LE Security.
+         *
+         * @return {@link OobData} with given builder values
+         *
+         * @throws IllegalStateException if either of the 2 required fields were not set.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public OobData build() {
+            final OobData oob =
+                    new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole,
+                            this.mConfirmationHash);
+
+            // If we have values, set them, otherwise use default
+            oob.mLeTemporaryKey =
+                    (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
+            oob.mLeAppearance = (this.mLeAppearance != null)
+                    ? this.mLeAppearance : oob.mLeAppearance;
+            oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
+            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+            oob.mRandomizerHash = this.mRandomizerHash;
+            return oob;
+        }
     }
 
-    public byte[] getLeSecureConnectionsConfirmation() {
-        return mLeSecureConnectionsConfirmation;
+    /**
+     * Builds an {@link OobData} object and validates that the required combination
+     * of values are present to create the Classic specific OobData type.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class ClassicBuilder {
+        // Used by both Classic and LE
+        /**
+         * It is recommended that this Hash C is generated anew for each
+         * pairing.
+         *
+         * <p>It should be noted that on passive NFC this isn't possible as the data is static
+         * and immutable.
+         *
+         * @hide
+         */
+        private byte[] mConfirmationHash = null;
+
+        /**
+         * Optional, but adds more validity to the pairing.
+         *
+         * <p>If not present a value of 0 is assumed.
+         *
+         * @hide
+         */
+        private byte[] mRandomizerHash = new byte[] {
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        };
+
+        /**
+         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+         *
+         * <p>This is the name that may be displayed to the device user as part of the UI.
+         *
+         * @hide
+         */
+        private byte[] mDeviceName = null;
+
+        /**
+         * This length value provides the absolute length of total OOB data block used for
+         * Bluetooth BR/EDR
+         *
+         * <p>OOB communication, which includes the length field itself and the Bluetooth
+         * Device Address.
+         *
+         * <p>The minimum length that may be represented in this field is 8.
+         *
+         * @hide
+         */
+        private final byte[] mClassicLength;
+
+        /**
+         * The Bluetooth Device Address is the address to which the OOB data belongs.
+         *
+         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+         *
+         * <p> Address is encoded in Little Endian order.
+         *
+         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+         *
+         * @hide
+         */
+        private final byte[] mDeviceAddressWithType;
+
+        /**
+         * Class of Device information is to be used to provide a graphical representation
+         * to the user as part of UI involving operations.
+         *
+         * <p>This is not to be used to determine a particular service can be used.
+         *
+         * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+         *
+         * @hide
+         */
+        private byte[] mClassOfDevice = null;
+
+        /**
+         * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+         * octets of data. Data is derived from controller/host stack and is required for pairing
+         * OOB.
+         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+         * of data. Data is derived from controller/host stack and is required
+         * for pairing OOB. Also, randomizerHash may be all 0s or null in which case
+         * it becomes all 0s.
+         * @param classicLength byte array representing the length of data from 8-65535 across 2
+         * octets (0xXXXX). Inclusive of this value in the length.
+         * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+         * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type
+         * as the last octet.
+         *
+         * @throws IllegalArgumentException if any value is not the correct length
+         * @throws NullPointerException if anything passed is null
+         *
+         * @hide
+         */
+        @SystemApi
+        private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength,
+                @NonNull byte[] deviceAddressWithType) {
+            Preconditions.checkNotNull(confirmationHash);
+            Preconditions.checkNotNull(classicLength);
+            Preconditions.checkNotNull(deviceAddressWithType);
+            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+                throw new IllegalArgumentException("confirmationHash must be "
+                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
+            }
+            this.mConfirmationHash = confirmationHash;
+            if (classicLength.length != OOB_LENGTH_OCTETS) {
+                throw new IllegalArgumentException("classicLength must be "
+                        + OOB_LENGTH_OCTETS + " octets in length.");
+            }
+            this.mClassicLength = classicLength;
+            if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
+                throw new IllegalArgumentException("deviceAddressWithType must be "
+                        + DEVICE_ADDRESS_OCTETS + " octets in length.");
+            }
+            this.mDeviceAddressWithType = deviceAddressWithType;
+        }
+
+        /**
+         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+         * of data. Data is derived from controller/host stack and is required for pairing OOB.
+         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+         *
+         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+         * @throws NullPointerException if randomizerHash is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+            Preconditions.checkNotNull(randomizerHash);
+            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+                throw new IllegalArgumentException("randomizerHash must be "
+                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
+            }
+            this.mRandomizerHash = randomizerHash;
+            return this;
+        }
+
+        /**
+         * Sets the Bluetooth Device name to be used for UI purposes.
+         *
+         * <p>Optional attribute.
+         *
+         * @param deviceName byte array representing the name, may be 0 in length, not null.
+         *
+         * @return {@link OobData#ClassicBuilder}
+         *
+         * @throws NullPointerException if deviceName is null
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
+            Preconditions.checkNotNull(deviceName);
+            this.mDeviceName = deviceName;
+            return this;
+        }
+
+        /**
+         * Sets the Bluetooth Class of Device; used for UI purposes only.
+         *
+         * <p>Not an indicator of available services!
+         *
+         * <p>Optional attribute.
+         *
+         * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+         *
+         * @return {@link OobData#ClassicBuilder}
+         *
+         * @throws IllegalArgumentException if length is not equal to
+         * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+         * @throws NullPointerException if classOfDevice is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
+            Preconditions.checkNotNull(classOfDevice);
+            if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
+                throw new IllegalArgumentException("classOfDevice must be "
+                        + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length.");
+            }
+            this.mClassOfDevice = classOfDevice;
+            return this;
+        }
+
+        /**
+         * Validates and builds the {@link OobDat object for Classic Security.
+         *
+         * @return {@link OobData} with previously given builder values.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public OobData build() {
+            final OobData oob =
+                    new OobData(this.mClassicLength, this.mDeviceAddressWithType,
+                            this.mConfirmationHash);
+            // If we have values, set them, otherwise use default
+            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+            oob.mClassOfDevice = (this.mClassOfDevice != null)
+                    ? this.mClassOfDevice : oob.mClassOfDevice;
+            oob.mRandomizerHash = this.mRandomizerHash;
+            return oob;
+        }
     }
 
-    public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) {
-        mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation;
+    // Members (Defaults for Optionals must be set or Parceling fails on NPE)
+    // Both
+    private final byte[] mDeviceAddressWithType;
+    private final byte[] mConfirmationHash;
+    private byte[] mRandomizerHash = new byte[] {
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    };
+    // Default the name to "Bluetooth Device"
+    private byte[] mDeviceName = new byte[] {
+        // Bluetooth
+        0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
+        // <space>Device
+        0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65
+    };
+
+    // Classic
+    private final byte[] mClassicLength;
+    private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
+
+    // LE
+    private final @LeRole int mLeDeviceRole;
+    private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
+    private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
+    private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
+
+    /**
+     * @return byte array representing the MAC address of a bluetooth device.
+     * The Address is 6 octets long with a 1 octet address type associated with the address.
+     *
+     * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type.
+     * For LE there are more choices for Address Type.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getDeviceAddressWithType() {
+        return mDeviceAddressWithType;
     }
 
-    public byte[] getLeSecureConnectionsRandom() {
-        return mLeSecureConnectionsRandom;
+    /**
+     * @return byte array representing the confirmationHash value
+     * which is used to confirm the identity to the controller.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getConfirmationHash() {
+        return mConfirmationHash;
     }
 
-    public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) {
-        mLeSecureConnectionsRandom = leSecureConnectionsRandom;
+    /**
+     * @return byte array representing the randomizerHash value
+     * which is used to verify the identity of the controller.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getRandomizerHash() {
+        return mRandomizerHash;
     }
 
-    public OobData() {
+    /**
+     * @return Device Name used for displaying name in UI.
+     *
+     * <p>Also, this will be populated with the LE Local Name if the data is for LE.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public byte[] getDeviceName() {
+        return mDeviceName;
+    }
+
+    /**
+     * @return byte array representing the oob data length which is the length
+     * of all of the data including these octets.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getClassicLength() {
+        return mClassicLength;
+    }
+
+    /**
+     * @return byte array representing the class of device for UI display.
+     *
+     * <p>Does not indicate services available; for display only.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getClassOfDevice() {
+        return mClassOfDevice;
+    }
+
+    /**
+     * @return Temporary Key used for LE pairing.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public byte[] getLeTemporaryKey() {
+        return mLeTemporaryKey;
+    }
+
+    /**
+     * @return Appearance used for LE pairing. For use in UI situations
+     * when determining what sort of icons or text to display regarding
+     * the device.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public byte[] getLeAppearance() {
+        return mLeTemporaryKey;
+    }
+
+    /**
+     * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability.
+     *
+     * <p>Possible LE Flags:
+     * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+     * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+     * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+     * LMP Feature Mask Definitions.
+     * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+     * Same Device Capable (Controller).
+     * Bit 49 of LMP Feature Mask Definitions.
+     * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+     * Same Device Capable (Host).
+     * Bit 55 of LMP Feature Mask Definitions.
+     * <b>0x05- 0x07 Reserved</b>
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @LeFlag
+    public int getLeFlags() {
+        return mLeFlags;
+    }
+
+    /**
+     * @return the supported and preferred roles of the LE device.
+     *
+     * <p>Possible Values:
+     * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+     * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+     * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+     * Peripheral Preferred
+     * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+     * 0x04 - 0xFF Reserved
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @LeRole
+    public int getLeDeviceRole() {
+        return mLeDeviceRole;
+    }
+
+    /**
+     * Classic Security Constructor
+     */
+    private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType,
+            @NonNull byte[] confirmationHash) {
+        mClassicLength = classicLength;
+        mDeviceAddressWithType = deviceAddressWithType;
+        mConfirmationHash = confirmationHash;
+        mLeDeviceRole = -1; // Satisfy final
+    }
+
+    /**
+     * LE Security Constructor
+     */
+    private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole,
+            @NonNull byte[] confirmationHash) {
+        mDeviceAddressWithType = deviceAddressWithType;
+        mLeDeviceRole = leDeviceRole;
+        mConfirmationHash = confirmationHash;
+        mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
     }
 
     private OobData(Parcel in) {
-        mLeBluetoothDeviceAddress = in.createByteArray();
-        mSecurityManagerTk = in.createByteArray();
-        mLeSecureConnectionsConfirmation = in.createByteArray();
-        mLeSecureConnectionsRandom = in.createByteArray();
+        // Both
+        mDeviceAddressWithType = in.createByteArray();
+        mConfirmationHash = in.createByteArray();
+        mRandomizerHash = in.createByteArray();
+        mDeviceName = in.createByteArray();
+
+        // Classic
+        mClassicLength = in.createByteArray();
+        mClassOfDevice = in.createByteArray();
+
+        // LE
+        mLeDeviceRole = in.readInt();
+        mLeTemporaryKey = in.createByteArray();
+        mLeAppearance = in.createByteArray();
+        mLeFlags = in.readInt();
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /**
+     * @hide
+     */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeByteArray(mLeBluetoothDeviceAddress);
-        out.writeByteArray(mSecurityManagerTk);
-        out.writeByteArray(mLeSecureConnectionsConfirmation);
-        out.writeByteArray(mLeSecureConnectionsRandom);
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        // Both
+        // Required
+        out.writeByteArray(mDeviceAddressWithType);
+        // Required
+        out.writeByteArray(mConfirmationHash);
+        // Optional
+        out.writeByteArray(mRandomizerHash);
+        // Optional
+        out.writeByteArray(mDeviceName);
+
+        // Classic
+        // Required
+        out.writeByteArray(mClassicLength);
+        // Optional
+        out.writeByteArray(mClassOfDevice);
+
+        // LE
+        // Required
+        out.writeInt(mLeDeviceRole);
+        // Required
+        out.writeByteArray(mLeTemporaryKey);
+        // Optional
+        out.writeByteArray(mLeAppearance);
+        // Optional
+        out.writeInt(mLeFlags);
     }
 
+    // For Parcelable
     public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
             new Parcelable.Creator<OobData>() {
         public OobData createFromParcel(Parcel in) {
@@ -108,4 +970,47 @@
             return new OobData[size];
         }
     };
+
+    /**
+     * @return a {@link String} representation of the OobData object.
+     *
+     * @hide
+     */
+    @Override
+    @NonNull
+    public String toString() {
+        return "OobData: \n\t"
+            // Both
+            + "Device Address With Type: " +  toHexString(mDeviceAddressWithType) + "\n\t"
+            + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t"
+            + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t"
+            + "Device Name: " + toHexString(mDeviceName) + "\n\t"
+            // Classic
+            + "OobData Length: " +  toHexString(mClassicLength) + "\n\t"
+            + "Class of Device: " +  toHexString(mClassOfDevice) + "\n\t"
+            // LE
+            + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t"
+            + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t"
+            + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t"
+            + "LE Flags: " + toHexString(mLeFlags) + "\n\t";
+    }
+
+    @NonNull
+    private String toHexString(@NonNull int b) {
+        return toHexString(new byte[] {(byte) b});
+    }
+
+    @NonNull
+    private String toHexString(@NonNull byte b) {
+        return toHexString(new byte[] {b});
+    }
+
+    @NonNull
+    private String toHexString(@NonNull byte[] array) {
+        StringBuilder builder = new StringBuilder(array.length * 2);
+        for (byte b: array) {
+            builder.append(String.format("%02x", b));
+        }
+        return builder.toString();
+    }
 }
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index f3ecbf6..6ab1975 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -21,6 +21,7 @@
 import static android.content.ContentResolver.SCHEME_CONTENT;
 import static android.content.ContentResolver.SCHEME_FILE;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.res.AssetFileDescriptor;
@@ -38,6 +39,7 @@
 import android.text.style.URLSpan;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.textclassifier.TextLinks;
 
 import com.android.internal.util.ArrayUtils;
 
@@ -204,6 +206,7 @@
         Uri mUri;
         // Additional activity info resolved by the system
         ActivityInfo mActivityInfo;
+        private TextLinks mTextLinks;
 
         /** @hide */
         public Item(Item other) {
@@ -332,6 +335,29 @@
         }
 
         /**
+         * Returns the results of text classification run on the raw text contained in this item,
+         * if it was performed, and if any entities were found in the text. Classification is
+         * generally only performed on the first item in clip data, and only if the text is below a
+         * certain length.
+         *
+         * <p>Returns {@code null} if classification was not performed, or if no entities were
+         * found in the text.
+         *
+         * @see ClipDescription#getConfidenceScore(String)
+         */
+        @Nullable
+        public TextLinks getTextLinks() {
+            return mTextLinks;
+        }
+
+        /**
+         * @hide
+         */
+        public void setTextLinks(TextLinks textLinks) {
+            mTextLinks = textLinks;
+        }
+
+        /**
          * Turn this item into text, regardless of the type of data it
          * actually contains.
          *
@@ -1183,6 +1209,7 @@
             dest.writeTypedObject(item.mIntent, flags);
             dest.writeTypedObject(item.mUri, flags);
             dest.writeTypedObject(item.mActivityInfo, flags);
+            dest.writeTypedObject(item.mTextLinks, flags);
         }
     }
 
@@ -1201,8 +1228,10 @@
             Intent intent = in.readTypedObject(Intent.CREATOR);
             Uri uri = in.readTypedObject(Uri.CREATOR);
             ActivityInfo info = in.readTypedObject(ActivityInfo.CREATOR);
+            TextLinks textLinks = in.readTypedObject(TextLinks.CREATOR);
             Item item = new Item(text, htmlText, intent, uri);
             item.setActivityInfo(info);
+            item.setTextLinks(textLinks);
             mItems.add(item);
         }
     }
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index d48f832..f49362e 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -16,16 +16,25 @@
 
 package android.content;
 
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Map;
 
 /**
  * Meta-data describing the contents of a {@link ClipData}.  Provides enough
@@ -115,12 +124,39 @@
      */
     public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value =
+            { CLASSIFICATION_NOT_COMPLETE, CLASSIFICATION_NOT_PERFORMED, CLASSIFICATION_COMPLETE})
+    @interface ClassificationStatus {}
+
+    /**
+     * Value returned by {@link #getConfidenceScore(String)} if text classification has not been
+     * completed on the associated clip. This will be always be the case if the clip has not been
+     * copied to clipboard, or if there is no associated clip.
+     */
+    public static final int CLASSIFICATION_NOT_COMPLETE = 1;
+
+    /**
+     * Value returned by {@link #getConfidenceScore(String)} if text classification was not and will
+     * not be performed on the associated clip. This may be the case if the clip does not contain
+     * text in its first item, or if the text is too long.
+     */
+    public static final int CLASSIFICATION_NOT_PERFORMED = 2;
+
+    /**
+     * Value returned by {@link #getConfidenceScore(String)} if text classification has been
+     * completed.
+     */
+    public static final int CLASSIFICATION_COMPLETE = 3;
 
     final CharSequence mLabel;
     private final ArrayList<String> mMimeTypes;
     private PersistableBundle mExtras;
     private long mTimeStamp;
     private boolean mIsStyledText;
+    private final ArrayMap<String, Float> mEntityConfidence = new ArrayMap<>();
+    private int mClassificationStatus = CLASSIFICATION_NOT_COMPLETE;
 
     /**
      * Create a new clip.
@@ -346,6 +382,61 @@
         mIsStyledText = isStyledText;
     }
 
+    /**
+     * Sets the current status of text classification for the associated clip.
+     *
+     * @hide
+     */
+    public void setClassificationStatus(@ClassificationStatus int status) {
+        mClassificationStatus = status;
+    }
+
+    /**
+     * Returns a score indicating confidence that an instance of the given entity is present in the
+     * first item of the clip data, if that item is plain text and text classification has been
+     * performed. The value ranges from 0 (low confidence) to 1 (high confidence). 0 indicates that
+     * the entity was not found in the classified text.
+     *
+     * <p>Entities should be as defined in the {@link TextClassifier} class, such as
+     * {@link TextClassifier#TYPE_ADDRESS}, {@link TextClassifier#TYPE_URL}, or
+     * {@link TextClassifier#TYPE_EMAIL}.
+     *
+     * <p>If the result is positive for any entity, the full classification result as a
+     * {@link TextLinks} object may be obtained using the {@link ClipData.Item#getTextLinks()}
+     * method.
+     *
+     * @throws IllegalStateException if {@link #getClassificationStatus()} is not
+     * {@link #CLASSIFICATION_COMPLETE}
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(@NonNull @TextClassifier.EntityType String entity) {
+        if (mClassificationStatus != CLASSIFICATION_COMPLETE) {
+            throw new IllegalStateException("Classification not complete");
+        }
+        return mEntityConfidence.getOrDefault(entity, 0f);
+    }
+
+    /**
+     * Returns {@link #CLASSIFICATION_COMPLETE} if text classification has been performed on the
+     * associated {@link ClipData}. If this is the case then {@link #getConfidenceScore} may be used
+     * to retrieve information about entities within the text. Otherwise, returns
+     * {@link #CLASSIFICATION_NOT_COMPLETE} if classification has not yet returned results, or
+     * {@link #CLASSIFICATION_NOT_PERFORMED} if classification was not attempted (e.g. because the
+     * text was too long).
+     */
+    public @ClassificationStatus int getClassificationStatus() {
+        return mClassificationStatus;
+    }
+
+    /**
+     * @hide
+     */
+    public void setConfidenceScores(Map<String, Float> confidences) {
+        mEntityConfidence.clear();
+        mEntityConfidence.putAll(confidences);
+        mClassificationStatus = CLASSIFICATION_COMPLETE;
+    }
+
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
@@ -451,6 +542,23 @@
         dest.writePersistableBundle(mExtras);
         dest.writeLong(mTimeStamp);
         dest.writeBoolean(mIsStyledText);
+        dest.writeInt(mClassificationStatus);
+        dest.writeBundle(confidencesToBundle());
+    }
+
+    private Bundle confidencesToBundle() {
+        Bundle bundle = new Bundle();
+        int size = mEntityConfidence.size();
+        for (int i = 0; i < size; i++) {
+            bundle.putFloat(mEntityConfidence.keyAt(i), mEntityConfidence.valueAt(i));
+        }
+        return bundle;
+    }
+
+    private void readBundleToConfidences(Bundle bundle) {
+        for (String key : bundle.keySet()) {
+            mEntityConfidence.put(key, bundle.getFloat(key));
+        }
     }
 
     ClipDescription(Parcel in) {
@@ -459,6 +567,8 @@
         mExtras = in.readPersistableBundle();
         mTimeStamp = in.readLong();
         mIsStyledText = in.readBoolean();
+        mClassificationStatus = in.readInt();
+        readBundleToConfidences(in.readBundle());
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ClipDescription> CREATOR =
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 49248b5..73b4f62 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2621,6 +2621,48 @@
         return !TextUtils.isEmpty(uri.getUserInfo());
     }
 
+    /**
+     * Returns the given content URI explicitly associated with the given {@link UserHandle}.
+     *
+     * @param contentUri The content URI to be associated with a user handle.
+     * @param userHandle The user handle with which to associate the URI.
+     *
+     * @throws IllegalArgumentException if
+     * <ul>
+     *  <li>the given URI is not content URI (a content URI has {@link Uri#getScheme} equal to
+     *  {@link ContentResolver.SCHEME_CONTENT}) or</li>
+     *  <li>the given URI is already explicitly associated with a {@link UserHandle}, which is
+     *  different than the given one.</li>
+     *  </ul>
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static Uri createContentUriAsUser(
+            @NonNull Uri contentUri, @NonNull UserHandle userHandle) {
+        if (!ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
+            throw new IllegalArgumentException(String.format(
+                "Given URI [%s] is not a content URI: ", contentUri));
+        }
+
+        int userId = userHandle.getIdentifier();
+        if (uriHasUserId(contentUri)) {
+            if (String.valueOf(userId).equals(contentUri.getUserInfo())) {
+                return contentUri;
+            }
+            throw new IllegalArgumentException(String.format(
+                "Given URI [%s] already has a user ID, different from given user handle [%s]",
+                contentUri,
+                userId));
+        }
+
+        Uri.Builder builder = contentUri.buildUpon();
+        builder.encodedAuthority(
+                "" + userHandle.getIdentifier() + "@" + contentUri.getEncodedAuthority());
+        return builder.build();
+    }
+
     /** @hide */
     @UnsupportedAppUsage
     public static Uri maybeAddUserId(Uri uri, int userId) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index df5c58c..f8dd0e1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3531,6 +3531,7 @@
             VIBRATOR_SERVICE,
             //@hide: STATUS_BAR_SERVICE,
             CONNECTIVITY_SERVICE,
+            PAC_PROXY_SERVICE,
             VCN_MANAGEMENT_SERVICE,
             //@hide: IP_MEMORY_STORE_SERVICE,
             IPSEC_SERVICE,
@@ -3713,6 +3714,9 @@
      * usage statistics.
      * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")
      * <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.
+     * <dt> {@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification")
+     * <dd> A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing
+     * web domain approval state.
      * </dl>
      *
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -3794,6 +3798,8 @@
      * @see android.app.usage.NetworkStatsManager
      * @see android.os.HardwarePropertiesManager
      * @see #HARDWARE_PROPERTIES_SERVICE
+     * @see #DOMAIN_VERIFICATION_SERVICE
+     * @see android.content.pm.verify.domain.DomainVerificationManager
      */
     public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
 
@@ -3813,7 +3819,8 @@
      * {@link android.view.inputmethod.InputMethodManager},
      * {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
      * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
-     * {@link android.app.usage.NetworkStatsManager}.
+     * {@link android.app.usage.NetworkStatsManager},
+     * {@link android.content.pm.verify.domain.DomainVerificationManager}.
      * </p>
      *
      * <p>
@@ -4131,6 +4138,17 @@
     public static final String CONNECTIVITY_SERVICE = "connectivity";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.PacProxyManager} for handling management of
+     * pac proxy information.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.PacProxyManager
+     * @hide
+     */
+    public static final String PAC_PROXY_SERVICE = "pac_proxy";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
      * for managing Virtual Carrier Networks
      *
@@ -4833,10 +4851,20 @@
      * @hide
      */
     @TestApi
-    @SuppressLint("ServiceName")  // TODO: This should be renamed to POWER_WHITELIST_SERVICE
+    @Deprecated
+    @SuppressLint("ServiceName")
     public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
 
     /**
+     * System service name for the PowerExemptionManager.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @TestApi
+    public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.admin.DevicePolicyManager} for working with global
      * device policy management.
@@ -5535,12 +5563,13 @@
     public static final String GAME_SERVICE = "game";
 
     /**
-     * Use with {@link #getSystemService(String)} to access domain verification service.
+     * Use with {@link #getSystemService(String)} to access
+     * {@link android.content.pm.verify.domain.DomainVerificationManager} to retrieve approval and
+     * user state for declared web domains.
      *
      * @see #getSystemService(String)
-     * @hide
+     * @see android.content.pm.verify.domain.DomainVerificationManager
      */
-    @SystemApi
     public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index de17fda..5cf83ac 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1901,6 +1901,20 @@
             "android.intent.action.AUTO_REVOKE_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to manage unused apps (hibernated apps).
+     *
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_UNUSED_APPS =
+            "android.intent.action.MANAGE_UNUSED_APPS";
+
+    /**
      * Activity action: Launch UI to review permissions for an app.
      * The system uses this intent if permission review for apps not
      * supporting the new runtime permissions model is enabled. In
@@ -2172,6 +2186,29 @@
             "android.intent.action.REVIEW_PERMISSION_USAGE";
 
     /**
+     * Activity action: Launch UI to review the timeline history of permissions.
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group name
+     * that will be displayed by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     * <p class="note">
+     * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission.
+     * </p>
+     *
+     * @see #EXTRA_PERMISSION_GROUP_NAME
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_PERMISSION_HISTORY =
+            "android.intent.action.REVIEW_PERMISSION_HISTORY";
+
+    /**
      * Activity action: Launch UI to review ongoing app uses of permissions.
      * <p>
      * Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent
@@ -2331,6 +2368,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
     /**
      * Alarm Changed Action: This is broadcast when the AlarmClock
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 1c21b2a..47c333c 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -168,6 +169,7 @@
      *
      * @hide
      */
+    @TestApi
     public @Nullable String getPackageName() {
         return packageName;
     }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 5a17753..58f83a7 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1086,6 +1086,13 @@
      */
     public WindowLayout windowLayout;
 
+    /**
+     * Attribution tags for finer grained calls if a {@android.content.Context#sendBroadcast(Intent,
+     * String)} is used with a permission.
+     * @hide
+     */
+    public String[] attributionTags;
+
     public ActivityInfo() {
     }
 
@@ -1114,6 +1121,7 @@
         maxAspectRatio = orig.maxAspectRatio;
         minAspectRatio = orig.minAspectRatio;
         supportsSizeChanges = orig.supportsSizeChanges;
+        attributionTags = orig.attributionTags;
     }
 
     /**
@@ -1361,6 +1369,15 @@
         if (supportsSizeChanges) {
             pw.println(prefix + "supportsSizeChanges=true");
         }
+        if (attributionTags != null && attributionTags.length > 0) {
+            StringBuilder tags = new StringBuilder();
+            tags.append(attributionTags[0]);
+            for (int i = 1; i < attributionTags.length; i++) {
+                tags.append(", ");
+                tags.append(attributionTags[i]);
+            }
+            pw.println(prefix + "attributionTags=[" + tags + "]");
+        }
         super.dumpBack(pw, prefix, dumpFlags);
     }
 
@@ -1406,6 +1423,7 @@
         dest.writeFloat(maxAspectRatio);
         dest.writeFloat(minAspectRatio);
         dest.writeBoolean(supportsSizeChanges);
+        dest.writeString8Array(attributionTags);
     }
 
     /**
@@ -1525,6 +1543,7 @@
         maxAspectRatio = source.readFloat();
         minAspectRatio = source.readFloat();
         supportsSizeChanges = source.readBoolean();
+        attributionTags = source.createString8Array();
     }
 
     /**
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index 745c39b..79b70f2 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -23,32 +23,34 @@
 oneway interface IDataLoaderStatusListener {
     /** The DataLoader process died, binder disconnected or class destroyed. */
     const int DATA_LOADER_DESTROYED = 0;
+    /** The system is in process of binding to the DataLoader. */
+    const int DATA_LOADER_BINDING = 1;
     /** DataLoader process is running and bound to. */
-    const int DATA_LOADER_BOUND = 1;
+    const int DATA_LOADER_BOUND = 2;
     /** DataLoader has handled onCreate(). */
-    const int DATA_LOADER_CREATED = 2;
+    const int DATA_LOADER_CREATED = 3;
 
     /** DataLoader can receive missing pages and read pages notifications,
      *  and ready to provide data. */
-    const int DATA_LOADER_STARTED = 3;
+    const int DATA_LOADER_STARTED = 4;
     /** DataLoader no longer ready to provide data and is not receiving
     *   any notifications from IncFS. */
-    const int DATA_LOADER_STOPPED = 4;
+    const int DATA_LOADER_STOPPED = 5;
 
     /** DataLoader streamed everything necessary to continue installation. */
-    const int DATA_LOADER_IMAGE_READY = 5;
+    const int DATA_LOADER_IMAGE_READY = 6;
     /** Installation can't continue as DataLoader failed to stream necessary data. */
-    const int DATA_LOADER_IMAGE_NOT_READY = 6;
+    const int DATA_LOADER_IMAGE_NOT_READY = 7;
 
     /** DataLoader instance can't run at the moment, but might recover later.
      *  It's up to system to decide if the app is still usable. */
-    const int DATA_LOADER_UNAVAILABLE = 7;
+    const int DATA_LOADER_UNAVAILABLE = 8;
 
     /** DataLoader reports that this instance is invalid and can never be restored.
     *   Warning: this is a terminal status that data loader should use carefully and
     *            the system should almost never use - e.g. only if all recovery attempts
     *            fail and all retry limits are exceeded. */
-    const int DATA_LOADER_UNRECOVERABLE = 8;
+    const int DATA_LOADER_UNRECOVERABLE = 9;
 
     /** There are no known issues with the data stream. */
     const int STREAM_HEALTHY = 0;
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 0d5b33c..8f9a0d7 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -29,6 +29,7 @@
 import android.util.jar.StrictJarFile;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.security.VerityUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -76,7 +77,8 @@
      * Returns whether fs-verity is required to install a dex metadata
      */
     public static boolean isFsVerityRequired() {
-        return SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
+        return VerityUtils.isFsVeritySupported()
+                && SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 9a84ded..b660a00 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -482,6 +482,7 @@
         ai.rotationAnimation = a.getRotationAnimation();
         ai.colorMode = a.getColorMode();
         ai.windowLayout = a.getWindowLayout();
+        ai.attributionTags = a.getAttributionTags();
         if ((flags & PackageManager.GET_META_DATA) != 0) {
             ai.metaData = a.getMetaData();
         }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 6f478ac..9285ccb 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -82,6 +82,9 @@
     @Nullable
     ActivityInfo.WindowLayout windowLayout;
 
+    @Nullable
+    String[] attributionTags;
+
     public ParsedActivity(ParsedActivity other) {
         super(other);
         this.theme = other.theme;
@@ -107,6 +110,7 @@
         this.rotationAnimation = other.rotationAnimation;
         this.colorMode = other.colorMode;
         this.windowLayout = other.windowLayout;
+        this.attributionTags = other.attributionTags;
     }
 
     /**
@@ -172,6 +176,7 @@
         alias.requestedVrComponent = target.requestedVrComponent;
         alias.directBootAware = target.directBootAware;
         alias.setProcessName(target.getProcessName());
+        alias.attributionTags = target.attributionTags;
         return alias;
 
         // Not all attributes from the target ParsedActivity are copied to the alias.
@@ -299,6 +304,7 @@
         } else {
             dest.writeBoolean(false);
         }
+        dest.writeString8Array(this.attributionTags);
     }
 
     public ParsedActivity() {
@@ -332,6 +338,7 @@
         if (in.readBoolean()) {
             windowLayout = new ActivityInfo.WindowLayout(in);
         }
+        this.attributionTags = in.createString8Array();
     }
 
     public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
@@ -445,4 +452,9 @@
     public ActivityInfo.WindowLayout getWindowLayout() {
         return windowLayout;
     }
+
+    @Nullable
+    public String[] getAttributionTags() {
+        return attributionTags;
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 0f4aa06..d99c410 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -210,6 +210,11 @@
                 pkg.setVisibleToInstantApps(true);
             }
 
+            String attributionTags = sa.getString(R.styleable.AndroidManifestActivity_attributionTags);
+            if (attributionTags != null) {
+                activity.attributionTags = attributionTags.split("\\|");
+            }
+
             return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                     false /*isAlias*/, visibleToEphemeral, input,
                     R.styleable.AndroidManifestActivity_parentActivityName,
diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java
index e24aeb2..8fa2352 100644
--- a/core/java/android/ddm/DdmHandleHeap.java
+++ b/core/java/android/ddm/DdmHandleHeap.java
@@ -30,15 +30,7 @@
  */
 public class DdmHandleHeap extends ChunkHandler {
 
-    public static final int CHUNK_HPIF = type("HPIF");
-    public static final int CHUNK_HPSG = type("HPSG");
-    public static final int CHUNK_HPDU = type("HPDU");
-    public static final int CHUNK_HPDS = type("HPDS");
-    public static final int CHUNK_NHSG = type("NHSG");
     public static final int CHUNK_HPGC = type("HPGC");
-    public static final int CHUNK_REAE = type("REAE");
-    public static final int CHUNK_REAQ = type("REAQ");
-    public static final int CHUNK_REAL = type("REAL");
 
     private static DdmHandleHeap mInstance = new DdmHandleHeap();
 
@@ -50,15 +42,7 @@
      * Register for the messages we're interested in.
      */
     public static void register() {
-        DdmServer.registerHandler(CHUNK_HPIF, mInstance);
-        DdmServer.registerHandler(CHUNK_HPSG, mInstance);
-        DdmServer.registerHandler(CHUNK_HPDU, mInstance);
-        DdmServer.registerHandler(CHUNK_HPDS, mInstance);
-        DdmServer.registerHandler(CHUNK_NHSG, mInstance);
         DdmServer.registerHandler(CHUNK_HPGC, mInstance);
-        DdmServer.registerHandler(CHUNK_REAE, mInstance);
-        DdmServer.registerHandler(CHUNK_REAQ, mInstance);
-        DdmServer.registerHandler(CHUNK_REAL, mInstance);
     }
 
     /**
@@ -81,24 +65,8 @@
             Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
         int type = request.type;
 
-        if (type == CHUNK_HPIF) {
-            return handleHPIF(request);
-        } else if (type == CHUNK_HPSG) {
-            return handleHPSGNHSG(request, false);
-        } else if (type == CHUNK_HPDU) {
-            return handleHPDU(request);
-        } else if (type == CHUNK_HPDS) {
-            return handleHPDS(request);
-        } else if (type == CHUNK_NHSG) {
-            return handleHPSGNHSG(request, true);
-        } else if (type == CHUNK_HPGC) {
+        if (type == CHUNK_HPGC) {
             return handleHPGC(request);
-        } else if (type == CHUNK_REAE) {
-            return handleREAE(request);
-        } else if (type == CHUNK_REAQ) {
-            return handleREAQ(request);
-        } else if (type == CHUNK_REAL) {
-            return handleREAL(request);
         } else {
             throw new RuntimeException("Unknown packet "
                 + ChunkHandler.name(type));
@@ -106,112 +74,6 @@
     }
 
     /*
-     * Handle a "HeaP InFo" request.
-     */
-    private Chunk handleHPIF(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-
-        int when = in.get();
-        if (false)
-            Log.v("ddm-heap", "Heap segment enable: when=" + when);
-
-        boolean ok = DdmVmInternal.heapInfoNotify(when);
-        if (!ok) {
-            return createFailChunk(1, "Unsupported HPIF what");
-        } else {
-            return null;        // empty response
-        }
-    }
-
-    /*
-     * Handle a "HeaP SeGment" or "Native Heap SeGment" request.
-     */
-    private Chunk handleHPSGNHSG(Chunk request, boolean isNative) {
-        ByteBuffer in = wrapChunk(request);
-
-        int when = in.get();
-        int what = in.get();
-        if (false)
-            Log.v("ddm-heap", "Heap segment enable: when=" + when
-                + ", what=" + what + ", isNative=" + isNative);
-
-        boolean ok = DdmVmInternal.heapSegmentNotify(when, what, isNative);
-        if (!ok) {
-            return createFailChunk(1, "Unsupported HPSG what/when");
-        } else {
-            // TODO: if "when" is non-zero and we want to see a dump
-            //       right away, initiate a GC.
-            return null;        // empty response
-        }
-    }
-
-    /*
-     * Handle a "HeaP DUmp" request.
-     *
-     * This currently just returns a result code.  We could pull up
-     * the entire contents of the file and return them, but hprof dump
-     * files can be a few megabytes.
-     */
-    private Chunk handleHPDU(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        byte result;
-
-        /* get the filename for the output file */
-        int len = in.getInt();
-        String fileName = getString(in, len);
-        if (false)
-            Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
-
-        try {
-            Debug.dumpHprofData(fileName);
-            result = 0;
-        } catch (UnsupportedOperationException uoe) {
-            Log.w("ddm-heap", "hprof dumps not supported in this VM");
-            result = -1;
-        } catch (IOException ioe) {
-            result = -1;
-        } catch (RuntimeException re) {
-            result = -1;
-        }
-
-        /* create a non-empty reply so the handler fires on completion */
-        byte[] reply = { result };
-        return new Chunk(CHUNK_HPDU, reply, 0, reply.length);
-    }
-
-    /*
-     * Handle a "HeaP Dump Streaming" request.
-     *
-     * This tells the VM to create a heap dump and send it directly to
-     * DDMS.  The dumps are large enough that we don't want to copy the
-     * data into a byte[] and send it from here.
-     */
-    private Chunk handleHPDS(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        byte result;
-
-        /* get the filename for the output file */
-        if (false)
-            Log.d("ddm-heap", "Heap dump: [DDMS]");
-
-        String failMsg = null;
-        try {
-            Debug.dumpHprofDataDdms();
-        } catch (UnsupportedOperationException uoe) {
-            failMsg = "hprof dumps not supported in this VM";
-        } catch (RuntimeException re) {
-            failMsg = "Exception: " + re.getMessage();
-        }
-
-        if (failMsg != null) {
-            Log.w("ddm-heap", failMsg);
-            return createFailChunk(1, failMsg);
-        } else {
-            return null;
-        }
-    }
-
-    /*
      * Handle a "HeaP Garbage Collection" request.
      */
     private Chunk handleHPGC(Chunk request) {
@@ -223,47 +85,4 @@
 
         return null;        // empty response
     }
-
-    /*
-     * Handle a "REcent Allocation Enable" request.
-     */
-    private Chunk handleREAE(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        boolean enable;
-
-        enable = (in.get() != 0);
-
-        if (false)
-            Log.d("ddm-heap", "Recent allocation enable request: " + enable);
-
-        DdmVmInternal.enableRecentAllocations(enable);
-
-        return null;        // empty response
-    }
-
-    /*
-     * Handle a "REcent Allocation Query" request.
-     */
-    private Chunk handleREAQ(Chunk request) {
-        //ByteBuffer in = wrapChunk(request);
-
-        byte[] reply = new byte[1];
-        reply[0] = DdmVmInternal.getRecentAllocationStatus() ? (byte)1 :(byte)0;
-        return new Chunk(CHUNK_REAQ, reply, 0, reply.length);
-    }
-
-    /*
-     * Handle a "REcent ALlocations" request.
-     */
-    private Chunk handleREAL(Chunk request) {
-        //ByteBuffer in = wrapChunk(request);
-
-        if (false)
-            Log.d("ddm-heap", "Recent allocations request");
-
-        /* generate the reply in a ready-to-go format */
-        byte[] reply = DdmVmInternal.getRecentAllocations();
-        return new Chunk(CHUNK_REAL, reply, 0, reply.length);
-    }
 }
-
diff --git a/core/java/android/ddm/DdmHandleThread.java b/core/java/android/ddm/DdmHandleThread.java
deleted file mode 100644
index 613ab75..0000000
--- a/core/java/android/ddm/DdmHandleThread.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2007 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.ddm;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
-import android.util.Log;
-import java.nio.ByteBuffer;
-
-/**
- * Handle thread-related traffic.
- */
-public class DdmHandleThread extends ChunkHandler {
-
-    public static final int CHUNK_THEN = type("THEN");
-    public static final int CHUNK_THCR = type("THCR");
-    public static final int CHUNK_THDE = type("THDE");
-    public static final int CHUNK_THST = type("THST");
-    public static final int CHUNK_STKL = type("STKL");
-
-    private static DdmHandleThread mInstance = new DdmHandleThread();
-
-
-    /* singleton, do not instantiate */
-    private DdmHandleThread() {}
-
-    /**
-     * Register for the messages we're interested in.
-     */
-    public static void register() {
-        DdmServer.registerHandler(CHUNK_THEN, mInstance);
-        DdmServer.registerHandler(CHUNK_THST, mInstance);
-        DdmServer.registerHandler(CHUNK_STKL, mInstance);
-    }
-
-    /**
-     * Called when the DDM server connects.  The handler is allowed to
-     * send messages to the server.
-     */
-    public void connected() {}
-
-    /**
-     * Called when the DDM server disconnects.  Can be used to disable
-     * periodic transmissions or clean up saved state.
-     */
-    public void disconnected() {}
-
-    /**
-     * Handle a chunk of data.
-     */
-    public Chunk handleChunk(Chunk request) {
-        if (false)
-            Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
-        int type = request.type;
-
-        if (type == CHUNK_THEN) {
-            return handleTHEN(request);
-        } else if (type == CHUNK_THST) {
-            return handleTHST(request);
-        } else if (type == CHUNK_STKL) {
-            return handleSTKL(request);
-        } else {
-            throw new RuntimeException("Unknown packet "
-                + ChunkHandler.name(type));
-        }
-    }
-
-    /*
-     * Handle a "THread notification ENable" request.
-     */
-    private Chunk handleTHEN(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-
-        boolean enable = (in.get() != 0);
-        //Log.i("ddm-thread", "Thread notify enable: " + enable);
-
-        DdmVmInternal.threadNotify(enable);
-        return null;        // empty response
-    }
-
-    /*
-     * Handle a "THread STatus" request.  This is constructed by the VM.
-     */
-    private Chunk handleTHST(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        // currently nothing to read from "in"
-
-        //Log.d("ddm-thread", "Thread status request");
-
-        byte[] status = DdmVmInternal.getThreadStats();
-        if (status != null)
-            return new Chunk(CHUNK_THST, status, 0, status.length);
-        else
-            return createFailChunk(1, "Can't build THST chunk");
-    }
-
-    /*
-     * Handle a STacK List request.
-     *
-     * This is done by threadId, which isn't great since those are
-     * recycled.  We need a thread serial ID.  The Linux tid is an okay
-     * answer as it's unlikely to recycle at the exact wrong moment.
-     * However, we're using the short threadId in THST messages, so we
-     * use them here for consistency.  (One thought is to keep the current
-     * thread ID in the low 16 bits and somehow serialize the top 16 bits.)
-     */
-    private Chunk handleSTKL(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        int threadId;
-
-        threadId = in.getInt();
-
-        //Log.d("ddm-thread", "Stack list request " + threadId);
-
-        StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId);
-        if (trace == null) {
-            return createFailChunk(1, "Stack trace unavailable");
-        } else {
-            return createStackChunk(trace, threadId);
-        }
-    }
-
-    /*
-     * Serialize a StackTraceElement[] into an STKL chunk.
-     *
-     * We include the threadId in the response so the other side doesn't have
-     * to match up requests and responses as carefully.
-     */
-    private Chunk createStackChunk(StackTraceElement[] trace, int threadId) {
-        int bufferSize = 0;
-
-        bufferSize += 4;            // version, flags, whatever
-        bufferSize += 4;            // thread ID
-        bufferSize += 4;            // frame count
-        for (StackTraceElement elem : trace) {
-            bufferSize += 4 + elem.getClassName().length() * 2;
-            bufferSize += 4 + elem.getMethodName().length() * 2;
-            bufferSize += 4;
-            if (elem.getFileName() != null)
-                bufferSize += elem.getFileName().length() * 2;
-            bufferSize += 4;        // line number
-        }
-
-        ByteBuffer out = ByteBuffer.allocate(bufferSize);
-        out.putInt(0);
-        out.putInt(threadId);
-        out.putInt(trace.length);
-        for (StackTraceElement elem : trace) {
-            out.putInt(elem.getClassName().length());
-            putString(out, elem.getClassName());
-            out.putInt(elem.getMethodName().length());
-            putString(out, elem.getMethodName());
-            if (elem.getFileName() != null) {
-                out.putInt(elem.getFileName().length());
-                putString(out, elem.getFileName());
-            } else {
-                out.putInt(0);
-            }
-            out.putInt(elem.getLineNumber());
-        }
-
-        return new Chunk(CHUNK_STKL, out);
-    }
-}
-
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index e0faa51..ca10312 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -16,9 +16,10 @@
 
 package android.ddm;
 
-import org.apache.harmony.dalvik.ddmc.DdmServer;
 import android.util.Log;
 
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
 /**
  * Just a place to stick handler registrations, instead of scattering
  * them around.
@@ -46,7 +47,6 @@
         if (false)
             Log.v("ddm", "Registering DDM message handlers");
         DdmHandleHello.register();
-        DdmHandleThread.register();
         DdmHandleHeap.register();
         DdmHandleNativeHeap.register();
         DdmHandleProfiling.register();
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index 25758e9..8c7695a 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -72,6 +72,45 @@
      * A font family definition.
      */
     public static final class FontFamily {
+
+        /**
+         * Builds a {@link FontFamily}.
+         */
+        public static final class Builder {
+            @NonNull private final String mName;
+            @NonNull private final List<Font> mFonts;
+
+            /**
+             * Constructs a {@link FontFamily.Builder}.
+             */
+            public Builder(@NonNull String name, @NonNull List<Font> fonts) {
+                Objects.requireNonNull(name);
+                Preconditions.checkStringNotEmpty(name);
+                Objects.requireNonNull(fonts);
+                Preconditions.checkCollectionElementsNotNull(fonts, "fonts");
+                Preconditions.checkCollectionNotEmpty(fonts, "fonts");
+                mName = name;
+                mFonts = new ArrayList<>(fonts);
+            }
+
+            /**
+             * Adds a {@link Font} to the builder.
+             *
+             * @return This builder object.
+             */
+            public @NonNull Builder addFont(@NonNull Font font) {
+                mFonts.add(font);
+                return this;
+            }
+
+            /**
+             * Builds a {@link FontFamily}.
+             */
+            public @NonNull FontFamily build() {
+                return new FontFamily(mName, mFonts);
+            }
+        }
+
         @NonNull
         private final String mName;
         @NonNull
@@ -90,12 +129,7 @@
          * @see android.graphics.Typeface#create(String, int)
          * @see Font
          */
-        public FontFamily(@NonNull String name, @NonNull List<Font> fonts) {
-            Objects.requireNonNull(name);
-            Preconditions.checkStringNotEmpty(name);
-            Objects.requireNonNull(fonts);
-            Preconditions.checkCollectionElementsNotNull(fonts, "fonts");
-            Preconditions.checkCollectionNotEmpty(fonts, "fonts");
+        private FontFamily(@NonNull String name, @NonNull List<Font> fonts) {
             mName = name;
             mFonts = fonts;
         }
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index 7bf692f..fa2ccbc 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -195,6 +195,7 @@
      * @return The current font configuration. null if failed to fetch information from the system
      *         service.
      */
+    @RequiresPermission(Manifest.permission.UPDATE_FONTS)
     public @NonNull FontConfig getFontConfig() {
         try {
             return mIFontManager.getFontConfig();
diff --git a/core/java/android/hardware/SensorPrivacyManagerInternal.java b/core/java/android/hardware/SensorPrivacyManagerInternal.java
new file mode 100644
index 0000000..d12e9f8
--- /dev/null
+++ b/core/java/android/hardware/SensorPrivacyManagerInternal.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+/**
+ * SensorPrivacyManager calls for within the system server
+ * @hide
+ */
+public abstract class SensorPrivacyManagerInternal {
+
+    /**
+     * A class implementing this interface can register to receive a callback when state changes.
+     */
+    public interface OnSensorPrivacyChangedListener {
+        /**
+         * The callback invoked when the state changes.
+         */
+        void onSensorPrivacyChanged(boolean enabled);
+    }
+
+    /**
+     * A class implementing this interface can register to receive a callback when state changes for
+     * any user.
+     */
+    public interface OnUserSensorPrivacyChangedListener {
+        /**
+         * The callback invoked when the state changes.
+         */
+        void onSensorPrivacyChanged(int userId, boolean enabled);
+    }
+
+    /**
+     * Get the individual sensor privacy state for a given user.
+     */
+    public abstract boolean isSensorPrivacyEnabled(int userId, int sensor);
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes.
+     */
+    public abstract void addSensorPrivacyListener(int userId, int sensor,
+            OnSensorPrivacyChangedListener listener);
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes for any user.
+     */
+    public abstract void addSensorPrivacyListenerForAllUsers(int sensor,
+            OnUserSensorPrivacyChangedListener listener);
+}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 788afe3..365dea6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -65,7 +65,7 @@
     private static final int CAPPED_SAMPLING_RATE_LEVEL = SensorDirectChannel.RATE_NORMAL;
 
     private static final String HIGH_SAMPLING_RATE_SENSORS_PERMISSION =
-                                        "android.permisison.HIGH_SAMPLING_RATE_SENSORS";
+                                        "android.permission.HIGH_SAMPLING_RATE_SENSORS";
     /**
      * For apps targeting S and above, a SecurityException is thrown when they do not have
      * HIGH_SAMPLING_RATE_SENSORS permission, run in debug mode, and request sampling rates that
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 7dc1eaa..93e5a0e 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -52,7 +52,8 @@
                 || wakeDisplayGestureEnabled(user)
                 || pickupGestureEnabled(user)
                 || tapGestureEnabled(user)
-                || doubleTapGestureEnabled(user);
+                || doubleTapGestureEnabled(user)
+                || quickPickupSensorEnabled(user);
     }
 
     /** {@hide} */
@@ -100,6 +101,13 @@
     }
 
     /** {@hide} */
+    public boolean quickPickupSensorEnabled(int user) {
+        return boolSettingDefaultOff(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user)
+                && !TextUtils.isEmpty(quickPickupSensorType())
+                && !alwaysOnEnabled(user);
+    }
+
+    /** {@hide} */
     public boolean wakeScreenGestureAvailable() {
         return mContext.getResources()
                 .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
@@ -143,6 +151,11 @@
     }
 
     /** {@hide} */
+    public String quickPickupSensorType() {
+        return mContext.getResources().getString(R.string.config_quickPickupSensorType);
+    }
+
+    /** {@hide} */
     public boolean pulseOnLongPressEnabled(int user) {
         return pulseOnLongPressAvailable() && boolSettingDefaultOff(
                 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b90c728..ad71f15 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -461,6 +461,7 @@
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
      * @hide
      */
+    @SystemApi
     public static final int VOLUME_CONTROL_ENABLED = 1;
     /**
      * HDMI CEC disabled.
@@ -468,6 +469,7 @@
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
      * @hide
      */
+    @SystemApi
     public static final int VOLUME_CONTROL_DISABLED = 0;
     /**
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
@@ -486,12 +488,14 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED = 1;
     /**
      * TV Wake on One Touch Play disabled.
      *
      * @hide
      */
+    @SystemApi
     public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED = 0;
     /**
      * @hide
@@ -509,12 +513,14 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1;
     /**
      * Not sending &lt;Standby&gt; on sleep.
      *
      * @hide
      */
+    @SystemApi
     public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0;
     /**
      * @hide
@@ -759,6 +765,7 @@
      * @hide
      * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(int)
      */
+    @SystemApi
     public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE =
             "volume_control_enabled";
     /**
@@ -767,6 +774,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
             "tv_wake_on_one_touch_play";
     /**
@@ -775,6 +783,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP =
             "tv_send_standby_on_sleep";
     /**
@@ -1259,6 +1268,7 @@
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setHdmiCecVolumeControlEnabled(
             @VolumeControl int hdmiCecVolumeControlEnabled) {
@@ -1274,6 +1284,7 @@
      * Returns whether volume changes via HDMI CEC are enabled.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     @VolumeControl
     public int getHdmiCecVolumeControlEnabled() {
@@ -2155,6 +2166,7 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setTvWakeOnOneTouchPlay(@NonNull @TvWakeOnOneTouchPlay int value) {
         if (mService == null) {
@@ -2176,6 +2188,7 @@
      *
      * @hide
      */
+    @SystemApi
     @NonNull
     @TvWakeOnOneTouchPlay
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2199,6 +2212,7 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setTvSendStandbyOnSleep(@NonNull @TvSendStandbyOnSleep int value) {
         if (mService == null) {
@@ -2220,6 +2234,7 @@
      *
      * @hide
      */
+    @SystemApi
     @NonNull
     @TvSendStandbyOnSleep
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index a65f36b..eaa8bd4 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -60,7 +60,8 @@
     private static final String TAG = "ContextHubManager";
 
     /**
-     * An extra of type int describing the client's authorization state.
+     * An extra containing an int from {@link AuthorizationState} describing the client's
+     * authorization state.
      */
     public static final String EXTRA_CLIENT_AUTHORIZATION_STATE =
             "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
@@ -115,11 +116,9 @@
 
     /**
      * Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a
-     * nanoapp. The {@link ContextHubClient} must perform any cleanup with the nanoapp as soon as
-     * possible.
-     *
-     * Note that the time between this state event and {@link AUTHORIZATION_DENIED} must be enough
-     * for the {@link ContextHubClient} to send at least one message to the nanoapp.
+     * nanoapp. After receiving this state event, the {@link ContextHubClient} has one minute to
+     * perform any cleanup with the nanoapp such that the nanoapp is no longer performing work on
+     * behalf of the {@link ContextHubClient}.
      */
     public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1;
 
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9198eb7..5cfcd66 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -171,7 +171,7 @@
                 SomeArgs args = (SomeArgs) msg.obj;
                 try {
                     inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
-                            (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
+                            (IInputMethodPrivilegedOperations) args.arg2);
                 } finally {
                     args.recycle();
                 }
@@ -280,10 +280,9 @@
     @BinderThread
     @Override
     public void initializeInternal(IBinder token, int displayId,
-            IInputMethodPrivilegedOperations privOps, int configChanges) {
+            IInputMethodPrivilegedOperations privOps) {
         mCaller.executeOrSendMessage(
-                mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
-                        configChanges));
+                mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
     }
 
     @BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 40a0fc4..7e2be01 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -70,7 +70,6 @@
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -132,7 +131,6 @@
 import android.window.WindowMetricsHelper;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
@@ -515,8 +513,6 @@
     private boolean mIsAutomotive;
     private Handler mHandler;
     private boolean mImeSurfaceScheduledForRemoval;
-    private Configuration mLastKnownConfig;
-    private int mHandledConfigChanges;
 
     /**
      * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
@@ -592,13 +588,12 @@
         @MainThread
         @Override
         public final void initializeInternal(@NonNull IBinder token, int displayId,
-                IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+                IInputMethodPrivilegedOperations privilegedOperations) {
             if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
                 Log.w(TAG, "The token has already registered, ignore this initialization.");
                 return;
             }
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
-            mHandledConfigChanges = configChanges;
             mPrivOps.set(privilegedOperations);
             InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
             updateInputMethodDisplay(displayId);
@@ -826,9 +821,6 @@
                 setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
             }
             final boolean isVisible = isInputViewShown();
-            if (isVisible && getResources() != null) {
-                mLastKnownConfig = new Configuration(getResources().getConfiguration());
-            }
             final boolean visibilityChanged = isVisible != wasVisible;
             if (resultReceiver != null) {
                 resultReceiver.send(visibilityChanged
@@ -1436,30 +1428,10 @@
      * state: {@link #onStartInput} if input is active, and
      * {@link #onCreateInputView} and {@link #onStartInputView} and related
      * appropriate functions if the UI is displayed.
-     * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration
-     * changes themselves instead of being restarted with
-     * {@link android.R.styleable#InputMethod_configChanges}.
      */
     @Override public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        if (shouldImeRestartForConfig(newConfig)) {
-            resetStateForNewConfiguration();
-        }
-    }
-
-    /**
-     * @return {@code true} if {@link InputMethodService} needs to restart to handle
-     * .{@link #onConfigurationChanged(Configuration)}
-     */
-    @VisibleForTesting
-    boolean shouldImeRestartForConfig(@NonNull Configuration newConfig) {
-        if (mLastKnownConfig == null) {
-            return true;
-        }
-        // If the new config is the same as the config this Service is already running with,
-        // then don't bother calling resetStateForNewConfiguration.
-        int unhandledDiff = (mLastKnownConfig.diffPublicOnly(newConfig) & ~mHandledConfigChanges);
-        return unhandledDiff != 0;
+        resetStateForNewConfiguration();
     }
 
     private void resetStateForNewConfiguration() {
@@ -3209,17 +3181,7 @@
             requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
         }
     }
-
-    @VisibleForTesting
-    void setLastKnownConfig(@NonNull Configuration config) {
-        mLastKnownConfig = config;
-    }
-
-    @VisibleForTesting
-    void setHandledConfigChanges(int configChanges) {
-        mHandledConfigChanges = configChanges;
-    }
-
+    
     void startExtractingText(boolean inputChanged) {
         final ExtractEditText eet = mExtractEditText;
         if (eet != null && getCurrentInputStarted()
diff --git a/core/java/android/net/IPacProxyInstalledListener.aidl b/core/java/android/net/IPacProxyInstalledListener.aidl
new file mode 100644
index 0000000..b1f946e
--- /dev/null
+++ b/core/java/android/net/IPacProxyInstalledListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+import android.net.Network;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+oneway interface IPacProxyInstalledListener {
+    void onPacProxyInstalled(in Network network, in ProxyInfo proxy);
+}
diff --git a/core/java/android/net/IPacProxyManager.aidl b/core/java/android/net/IPacProxyManager.aidl
new file mode 100644
index 0000000..8f65c56
--- /dev/null
+++ b/core/java/android/net/IPacProxyManager.aidl
@@ -0,0 +1,28 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IPacProxyInstalledListener;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+interface IPacProxyManager
+{
+    void addListener(IPacProxyInstalledListener listener);
+    void removeListener(IPacProxyInstalledListener listener);
+    void setCurrentProxyScriptUrl(in ProxyInfo proxyInfo);
+}
diff --git a/core/java/android/net/PacProxyManager.java b/core/java/android/net/PacProxyManager.java
new file mode 100644
index 0000000..8f7ad8c
--- /dev/null
+++ b/core/java/android/net/PacProxyManager.java
@@ -0,0 +1,150 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@SystemService(Context.PAC_PROXY_SERVICE)
+public class PacProxyManager {
+    private final Context mContext;
+    private final IPacProxyManager mService;
+    @GuardedBy("mListenerMap")
+    private final HashMap<PacProxyInstalledListener, PacProxyInstalledListenerProxy>
+            mListenerMap = new HashMap<>();
+
+    /** @hide */
+    public PacProxyManager(Context context, IPacProxyManager service) {
+        Objects.requireNonNull(service, "missing IPacProxyManager");
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Add a listener to start monitoring events reported by PacProxyService.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void addPacProxyInstalledListener(@NonNull Executor executor,
+            @NonNull PacProxyInstalledListener listener) {
+        try {
+            synchronized (mListenerMap) {
+                final PacProxyInstalledListenerProxy listenerProxy =
+                        new PacProxyInstalledListenerProxy(executor, listener);
+
+                if (null != mListenerMap.putIfAbsent(listener, listenerProxy)) {
+                    throw new IllegalStateException("Listener is already added.");
+                }
+                mService.addListener(listenerProxy);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove the listener to stop monitoring the event of PacProxyInstalledListener.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void removePacProxyInstalledListener(@NonNull PacProxyInstalledListener listener) {
+        try {
+            synchronized (mListenerMap) {
+                final PacProxyInstalledListenerProxy listenerProxy = mListenerMap.remove(listener);
+                if (listenerProxy == null) return;
+                mService.removeListener(listenerProxy);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Updates the PAC Proxy Installer with current Proxy information.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+        try {
+            mService.setCurrentProxyScriptUrl(proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * A callback interface for monitoring changes of PAC proxy information.
+     */
+    public interface PacProxyInstalledListener {
+        /**
+         * Notify that the PAC proxy has been installed. Note that this method will be called with
+         * a ProxyInfo with an empty PAC URL when the PAC proxy is removed.
+         *
+         * This method supports different PAC proxies per-network but not all devices might support
+         * per-network proxies. In that case it will be applied globally.
+         *
+         * @param network the network for which this proxy installed.
+         * @param proxy the installed proxy.
+         */
+        void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy);
+    }
+
+    /**
+     * PacProxyInstalledListener proxy for PacProxyInstalledListener object.
+     * @hide
+     */
+    public class PacProxyInstalledListenerProxy extends IPacProxyInstalledListener.Stub {
+        private final Executor mExecutor;
+        private final PacProxyInstalledListener mListener;
+
+        PacProxyInstalledListenerProxy(Executor executor, PacProxyInstalledListener listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onPacProxyInstalled(Network network, ProxyInfo proxy) {
+            Binder.withCleanCallingIdentity(() -> {
+                mExecutor.execute(() -> {
+                    mListener.onPacProxyInstalled(network, proxy);
+                });
+            });
+        }
+    }
+}
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 326943a..84b7eec 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -51,7 +51,7 @@
                 ServiceManager.getService(PROXY_SERVICE));
         if (mProxyService == null) {
             // Added because of b10267814 where mako is restarting.
-            Log.e(TAG, "PacProxyInstaller: no proxy service");
+            Log.e(TAG, "PacProxyService: no proxy service");
         }
         mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
     }
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 8ebf757..062438c 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -73,7 +73,8 @@
 public class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
-    private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+    private static final Map<
+                    VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
 
     @NonNull private final Context mContext;
@@ -93,13 +94,13 @@
     }
 
     /**
-     * Get all currently registered VcnNetworkPolicyListeners for testing purposes.
+     * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes.
      *
      * @hide
      */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
-    public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+    public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             getAllPolicyListeners() {
         return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
     }
@@ -162,14 +163,14 @@
     }
 
     // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using
-    // the new VcnNetworkPolicyListener API
+    // the new VcnNetworkPolicyChangeListener API
     /**
      * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
      * can register to receive updates for VCN-underlying Network policies from the System Server.
      *
      * @hide
      */
-    public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {}
+    public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {}
 
     /**
      * Add a listener for VCN-underlying network policy updates.
@@ -185,7 +186,7 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public void addVcnUnderlyingNetworkPolicyListener(
             @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
-        addVcnNetworkPolicyListener(executor, listener);
+        addVcnNetworkPolicyChangeListener(executor, listener);
     }
 
     /**
@@ -198,7 +199,7 @@
      */
     public void removeVcnUnderlyingNetworkPolicyListener(
             @NonNull VcnUnderlyingNetworkPolicyListener listener) {
-        removeVcnNetworkPolicyListener(listener);
+        removeVcnNetworkPolicyChangeListener(listener);
     }
 
     /**
@@ -233,20 +234,20 @@
     }
 
     /**
-     * VcnNetworkPolicyListener is the interface through which internal system components (e.g.
-     * Network Factories) can register to receive updates for VCN-underlying Network policies from
-     * the System Server.
+     * VcnNetworkPolicyChangeListener is the interface through which internal system components
+     * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies
+     * from the System Server.
      *
      * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks
-     * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify
-     * the registrant when VCN Network policies change. Upon receiving this signal, the listener
-     * must check {@link VcnManager} for the current Network policy result for each of its Networks
-     * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+     * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to
+     * notify the registrant when VCN Network policies change. Upon receiving this signal, the
+     * listener must check {@link VcnManager} for the current Network policy result for each of its
+     * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
      *
      * @hide
      */
     @SystemApi
-    public interface VcnNetworkPolicyListener {
+    public interface VcnNetworkPolicyChangeListener {
         /**
          * Notifies the implementation that the VCN's underlying Network policy has changed.
          *
@@ -260,20 +261,21 @@
     /**
      * Add a listener for VCN-underlying Network policy updates.
      *
-     * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is
-     * registered. No callbacks are guaranteed upon registration.
+     * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it
+     * is registered. No callbacks are guaranteed upon registration.
      *
      * @param executor the Executor that will be used for invoking all calls to the specified
      *     Listener
-     * @param listener the VcnNetworkPolicyListener to be added
+     * @param listener the VcnNetworkPolicyChangeListener to be added
      * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
-     * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered
+     * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already
+     *     registered
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
-    public void addVcnNetworkPolicyListener(
-            @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) {
+    public void addVcnNetworkPolicyChangeListener(
+            @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) {
         requireNonNull(executor, "executor must not be null");
         requireNonNull(listener, "listener must not be null");
 
@@ -292,15 +294,18 @@
     }
 
     /**
-     * Remove the specified VcnNetworkPolicyListener from VcnManager.
+     * Remove the specified VcnNetworkPolicyChangeListener from VcnManager.
      *
      * <p>If the specified listener is not currently registered, this is a no-op.
      *
-     * @param listener the VcnNetworkPolicyListener that will be removed
+     * @param listener the VcnNetworkPolicyChangeListener that will be removed
+     * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
      * @hide
      */
     @SystemApi
-    public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) {
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void removeVcnNetworkPolicyChangeListener(
+            @NonNull VcnNetworkPolicyChangeListener listener) {
         requireNonNull(listener, "listener must not be null");
 
         VcnUnderlyingNetworkPolicyListenerBinder binder =
@@ -320,8 +325,9 @@
      * Applies the network policy for a {@link android.net.Network} with the given parameters.
      *
      * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
-     * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider
-     * MUST poll for the updated Network policy based on that Network's capabilities and properties.
+     * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network
+     * Provider MUST poll for the updated Network policy based on that Network's capabilities and
+     * properties.
      *
      * @param networkCapabilities the NetworkCapabilities to be used in determining the Network
      *     policy result for this Network.
@@ -532,17 +538,18 @@
     }
 
     /**
-     * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server.
+     * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System
+     * Server.
      *
      * @hide
      */
     private static class VcnUnderlyingNetworkPolicyListenerBinder
             extends IVcnUnderlyingNetworkPolicyListener.Stub {
         @NonNull private final Executor mExecutor;
-        @NonNull private final VcnNetworkPolicyListener mListener;
+        @NonNull private final VcnNetworkPolicyChangeListener mListener;
 
         private VcnUnderlyingNetworkPolicyListenerBinder(
-                Executor executor, VcnNetworkPolicyListener listener) {
+                Executor executor, VcnNetworkPolicyChangeListener listener) {
             mExecutor = executor;
             mListener = listener;
         }
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
index a86237d..97ec594 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -83,4 +83,29 @@
      * wait on the battery service lock.
      */
     public abstract int getInvalidCharger();
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     */
+    public abstract void setChargerAcOnline(boolean online, boolean forceUpdate);
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     */
+    public abstract void setBatteryLevel(int level, boolean forceUpdate);
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     */
+    public abstract void unplugBattery(boolean forceUpdate);
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     */
+    public abstract void resetBattery(boolean forceUpdate);
+
+    /**
+     * Suspend charging even if plugged in.
+     */
+    public abstract void suspendBatteryInput();
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66f7bd9..4c26e2f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -995,6 +995,15 @@
         public abstract long getScreenOnMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
+         * on device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+        /**
          * Returns the battery consumption (in microcoulombs) used by this uid for each
          * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
          * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
@@ -2521,6 +2530,15 @@
     public abstract long getScreenDozeMeasuredBatteryConsumptionUC();
 
     /**
+     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+    /**
      * Returns the battery consumption (in microcoulombs) that each
      * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
      * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}) consumed.
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 1905d70..e47478a 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.net.NetworkStack;
 import android.os.connectivity.CellularBatteryStats;
@@ -487,4 +488,74 @@
         return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
                 : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
     }
-}
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        try {
+            mBatteryStats.setChargerAcOnline(online, forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setBatteryLevel(int level, boolean forceUpdate) {
+        try {
+            mBatteryStats.setBatteryLevel(level, forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void unplugBattery(boolean forceUpdate) {
+        try {
+            mBatteryStats.unplugBattery(forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void resetBattery(boolean forceUpdate) {
+        try {
+            mBatteryStats.resetBattery(forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Suspend charging even if plugged in.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void suspendBatteryInput() {
+        try {
+            mBatteryStats.suspendBatteryInput();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 83f78a5..0d9f715 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -27,6 +27,7 @@
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.sysprop.DeviceProperties;
 import android.sysprop.SocProperties;
 import android.sysprop.TelephonyProperties;
 import android.text.TextUtils;
@@ -298,6 +299,19 @@
                 "ro.build.version.security_patch", "");
 
         /**
+         * The media performance class of the device or 0 if none.
+         * <p>
+         * If this value is not <code>0</code>, the device conforms to the media performance class
+         * definition of the SDK version of this value. This value never changes while a device is
+         * booted, but it may increase when the hardware manufacturer provides an OTA update.
+         * <p>
+         * Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
+         * {@link Build.VERSION_CODES#S}.
+         */
+        public static final int MEDIA_PERFORMANCE_CLASS =
+                DeviceProperties.media_performance_class().orElse(0);
+
+        /**
          * The user-visible SDK version of the framework in its raw String
          * representation; use {@link #SDK_INT} instead.
          *
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 124c0b0..21bf8b8 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -728,11 +728,11 @@
     /**
      * Standard directory in which to place any audio files that should be
      * in the regular list of music for the user.
-     * This may be combined with
+     * This may be combined with {@link #DIRECTORY_AUDIOBOOKS},
      * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
-     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_MUSIC = "Music";
 
@@ -741,10 +741,10 @@
      * in the list of podcasts that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_NOTIFICATIONS},
-     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_PODCASTS = "Podcasts";
 
@@ -753,10 +753,10 @@
      * in the list of ringtones that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
-     * {@link #DIRECTORY_ALARMS} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_RINGTONES = "Ringtones";
 
@@ -765,10 +765,10 @@
      * in the list of alarms that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
-     * and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_RINGTONES},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_ALARMS = "Alarms";
 
@@ -777,10 +777,10 @@
      * in the list of notifications that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_PODCASTS},
-     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_NOTIFICATIONS = "Notifications";
 
@@ -831,14 +831,26 @@
     public static String DIRECTORY_SCREENSHOTS = "Screenshots";
 
     /**
-     * Standard directory in which to place any audio files which are
-     * audiobooks.
+     * Standard directory in which to place any audio files that should be
+     * in the list of audiobooks that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
 
     /**
-     * Standard directory in which to place any audio files which are
-     * recordings.
+     * Standard directory in which to place any audio files that should be
+     * in the list of voice recordings recorded by voice recorder apps that
+     * the user can select (not as regular music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+     * and {@link #DIRECTORY_RINGTONES} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     @NonNull
     // The better way is that expose a static method getRecordingDirectories.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 786a7d0..a19728c 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -323,6 +323,13 @@
     public static final int USER_ACTIVITY_EVENT_ATTENTION = 4;
 
     /**
+     * User activity event type: {@link com.android.server.power.FaceDownDetector} taking action
+     * on behalf of user.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_EVENT_FACE_DOWN = 5;
+
+    /**
      * User activity flag: If already dimmed, extend the dim timeout
      * but do not brighten.  This flag is useful for keeping the screen on
      * a little longer without causing a visible change such as when
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index b003d23..b90d438 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -208,6 +208,28 @@
     public abstract boolean hasAmplitudeControl();
 
     /**
+     * Gets the resonant frequency of the vibrator.
+     *
+     * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+     *         this vibrator is a composite of multiple physical devices.
+     * @hide
+     */
+    public float getResonantFrequency() {
+        return Float.NaN;
+    }
+
+    /**
+     * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+     *
+     * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+     *         this vibrator is a composite of multiple physical devices.
+     * @hide
+     */
+    public float getQFactor() {
+        return Float.NaN;
+    }
+
+    /**
      * Configure an always-on haptics effect.
      *
      * @param alwaysOnId The board-specific always-on ID to configure.
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 50d2de3..3121b95 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -42,21 +42,27 @@
     private final SparseBooleanArray mSupportedEffects;
     @Nullable
     private final SparseBooleanArray mSupportedPrimitives;
+    private final float mResonantFrequency;
+    private final float mQFactor;
 
     VibratorInfo(Parcel in) {
         mId = in.readInt();
         mCapabilities = in.readLong();
         mSupportedEffects = in.readSparseBooleanArray();
         mSupportedPrimitives = in.readSparseBooleanArray();
+        mResonantFrequency = in.readFloat();
+        mQFactor = in.readFloat();
     }
 
     /** @hide */
     public VibratorInfo(int id, long capabilities, int[] supportedEffects,
-            int[] supportedPrimitives) {
+            int[] supportedPrimitives, float resonantFrequency, float qFactor) {
         mId = id;
         mCapabilities = capabilities;
         mSupportedEffects = toSparseBooleanArray(supportedEffects);
         mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
+        mResonantFrequency = resonantFrequency;
+        mQFactor = qFactor;
     }
 
     @Override
@@ -65,6 +71,8 @@
         dest.writeLong(mCapabilities);
         dest.writeSparseBooleanArray(mSupportedEffects);
         dest.writeSparseBooleanArray(mSupportedPrimitives);
+        dest.writeFloat(mResonantFrequency);
+        dest.writeFloat(mQFactor);
     }
 
     @Override
@@ -83,12 +91,15 @@
         VibratorInfo that = (VibratorInfo) o;
         return mId == that.mId && mCapabilities == that.mCapabilities
                 && Objects.equals(mSupportedEffects, that.mSupportedEffects)
-                && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives);
+                && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
+                && Objects.equals(mResonantFrequency, that.mResonantFrequency)
+                && Objects.equals(mQFactor, that.mQFactor);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives);
+        return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
+                mResonantFrequency, mQFactor);
     }
 
     @Override
@@ -99,6 +110,8 @@
                 + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
                 + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
                 + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+                + ", mResonantFrequency=" + mResonantFrequency
+                + ", mQFactor=" + mQFactor
                 + '}';
     }
 
@@ -156,6 +169,26 @@
         return (mCapabilities & capability) == capability;
     }
 
+    /**
+     * Gets the resonant frequency of the vibrator.
+     *
+     * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+     *         this vibrator is a composite of multiple physical devices.
+     */
+    public float getResonantFrequency() {
+        return mResonantFrequency;
+    }
+
+    /**
+     * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+     *
+     * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+     *         this vibrator is a composite of multiple physical devices.
+     */
+    public float getQFactor() {
+        return mQFactor;
+    }
+
     private String[] getCapabilitiesNames() {
         List<String> names = new ArrayList<>();
         if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 73520e0..2a42b98 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -51,6 +51,8 @@
 public final class IncrementalFileStorages {
     private static final String TAG = "IncrementalFileStorages";
 
+    private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
+
     private @NonNull final IncrementalManager mIncrementalManager;
     private @NonNull final File mStageDir;
     private @Nullable IncrementalStorage mInheritedStorage;
@@ -116,7 +118,10 @@
                 mInheritedStorage = mIncrementalManager.openStorage(
                         inheritedDir.getAbsolutePath());
                 if (mInheritedStorage != null) {
-                    if (!mInheritedStorage.isFullyLoaded()) {
+                    boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+                            dataLoaderParams.getComponentName().getPackageName());
+                    if (systemDataLoader && !mInheritedStorage.isFullyLoaded()) {
+                        // System data loader does not support incomplete storages.
                         throw new IOException("Inherited storage has missing pages.");
                     }
 
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index dc6f63a..047c05a 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -30,6 +30,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -265,6 +266,13 @@
     }
 
     /**
+     * Checks if an fd corresponds to a file on a mounted Incremental File System.
+     */
+    public static boolean isIncrementalFileFd(@NonNull FileDescriptor fd) {
+        return nativeIsIncrementalFd(fd.getInt$());
+    }
+
+    /**
      * Returns raw signature for file if it's on Incremental File System.
      * Unsafe, use only if you are sure what you are doing.
      */
@@ -421,9 +429,22 @@
         storage.unregisterStorageHealthListener();
     }
 
+    /**
+     * Returns the metrics of an Incremental Storage.
+     */
+    public IncrementalMetrics getMetrics(@NonNull String codePath) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return null;
+        }
+        return new IncrementalMetrics(storage.getMetrics());
+    }
+
     /* Native methods */
     private static native boolean nativeIsEnabled();
     private static native boolean nativeIsV2Available();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
+    private static native boolean nativeIsIncrementalFd(@NonNull int fd);
     private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
 }
diff --git a/core/java/android/os/incremental/IncrementalMetrics.java b/core/java/android/os/incremental/IncrementalMetrics.java
new file mode 100644
index 0000000..44dea1b
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalMetrics.java
@@ -0,0 +1,39 @@
+/*
+ * 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.incremental;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+/**
+ * Provides methods to access metrics about an app installed via Incremental
+ * @hide
+ */
+public class IncrementalMetrics {
+    @NonNull private final PersistableBundle mData;
+
+    public IncrementalMetrics(@NonNull PersistableBundle data) {
+        mData = data;
+    }
+
+    /**
+     * @return Milliseconds between now and when the oldest pending read happened
+     */
+    public long getMillisSinceOldestPendingRead() {
+        return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_OLDEST_PENDING_READ, -1);
+    }
+}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index e6ce8cd..7cf0144 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.IDataLoaderStatusListener;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 
 import java.io.File;
@@ -601,4 +602,17 @@
             return;
         }
     }
+
+    /**
+     * Returns the metrics of the current storage.
+     * {@see IIncrementalService} for metrics keys.
+     */
+    public PersistableBundle getMetrics() {
+        try {
+            return mService.getMetrics(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 396ba2d..82c4c71 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -52,6 +52,11 @@
     }
 
     /**
+     * Return true if fuse is mounted.
+     */
+    public abstract boolean isFuseMounted(int userId);
+
+    /**
      * Create storage directories if it does not exist.
      * Return true if the directories were setup correctly, otherwise false.
      */
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 4c9e77c..7e3a0f3 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -111,8 +111,7 @@
 
     private static boolean shouldShowLocationIndicator() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_LOCATION_INDICATORS_ENABLED, false)
-                || shouldShowPermissionsHub();
+                PROPERTY_LOCATION_INDICATORS_ENABLED, false);
     }
 
     private static long getRecentThreshold(Long now) {
@@ -326,10 +325,10 @@
                     }
 
                     if (packageName.equals(SYSTEM_PKG)
-                            || (!isUserSensitive(packageName, user, op)
+                            || (!shouldShowPermissionsHub()
+                            && !isUserSensitive(packageName, user, op)
                             && !isLocationProvider(packageName, user)
-                            && !isAppPredictor(packageName, user))
-                            && !isSpeechRecognizerUsage(op, packageName)) {
+                            && !isSpeechRecognizerUsage(op, packageName))) {
                         continue;
                     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4dfbb6f..5d139d9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8369,6 +8369,14 @@
         public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture";
 
         /**
+         * Gesture that wakes up the display on quick pickup, toggling between
+         * {@link Display.STATE_OFF} and {@link Display.STATE_DOZE}.
+         * @hide
+         */
+        @Readable
+        public static final String DOZE_QUICK_PICKUP_GESTURE = "doze_quick_pickup_gesture";
+
+        /**
          * Whether the device should suppress the current doze configuration and disable dozing.
          * @hide
          */
@@ -9869,6 +9877,14 @@
                 "reminder_exp_learning_event_count";
 
         /**
+         * Whether to show clipboard access notifications.
+         *
+         * @hide
+         */
+        public static final String CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS =
+                "clipboard_show_access_notifications";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 8a4812a..374de9c 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5310,5 +5310,12 @@
          * @hide
          */
         public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+
+        /**
+         * TelephonyProvider column name for device to device sharing status.
+         *
+         * @hide
+         */
+        public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
     }
 }
diff --git a/core/java/android/service/displayhash/DisplayHashParams.aidl b/core/java/android/service/displayhash/DisplayHashParams.aidl
new file mode 100644
index 0000000..90f9bf1
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHashParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.displayhash;
+
+parcelable DisplayHashParams;
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
new file mode 100644
index 0000000..6a176a33
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -0,0 +1,251 @@
+/*
+ * 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.service.displayhash;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Parcelable;
+import android.util.Size;
+import android.view.displayhash.DisplayHashResultCallback;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Information passed from the {@link DisplayHasherService} to system server about how to get the
+ * display data that will be used to generate the {@link android.view.displayhash.DisplayHash}
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(genAidl = true, genToString = true, genParcelable = true, genHiddenConstructor = true)
+public final class DisplayHashParams implements Parcelable {
+    /**
+     * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+     * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+     * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+     * If {@code null}, the buffer size will not be changed.
+     */
+    @Nullable
+    private final Size mBufferSize;
+
+    /**
+     * Whether the content captured will use filtering when scaling.
+     */
+    private final boolean mBufferScaleWithFiltering;
+
+    /**
+     * Whether the content will be captured in grayscale or color.
+     */
+    private final boolean mGrayscaleBuffer;
+
+    /**
+     * A builder for {@link DisplayHashParams}
+     */
+    public static final class Builder {
+        @Nullable
+        private Size mBufferSize;
+        private boolean mBufferScaleWithFiltering;
+        private boolean mGrayscaleBuffer;
+
+        /**
+         * Creates a new Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * The size to scale the buffer to so the hash algorithm can properly generate the hash.
+         */
+        @NonNull
+        public Builder setBufferSize(int w, int h) {
+            mBufferSize = new Size(w, h);
+            return this;
+        }
+
+        /**
+         * Whether the content captured will use filtering when scaling.
+         */
+        @NonNull
+        public Builder setBufferScaleWithFiltering(boolean value) {
+            mBufferScaleWithFiltering = value;
+            return this;
+        }
+
+        /**
+         * Whether the content will be captured in grayscale or color.
+         */
+        @NonNull
+        public Builder setGrayscaleBuffer(boolean value) {
+            mGrayscaleBuffer = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        @NonNull
+        public DisplayHashParams build() {
+            return new DisplayHashParams(mBufferSize, mBufferScaleWithFiltering, mGrayscaleBuffer);
+        }
+    }
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DisplayHashParams.
+     *
+     * @param bufferSize
+     *   The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+     *   buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+     *   Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+     *   If {@code null}, the buffer size will not be changed.
+     * @param bufferScaleWithFiltering
+     *   Whether the content captured will use filtering when scaling.
+     * @param grayscaleBuffer
+     *   Whether the content will be captured in grayscale or color.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public DisplayHashParams(
+            @Nullable Size bufferSize,
+            boolean bufferScaleWithFiltering,
+            boolean grayscaleBuffer) {
+        this.mBufferSize = bufferSize;
+        this.mBufferScaleWithFiltering = bufferScaleWithFiltering;
+        this.mGrayscaleBuffer = grayscaleBuffer;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The size to scale the buffer to so the hash algorithm can properly generate the hash. The
+     * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
+     * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
+     * If {@code null}, the buffer size will not be changed.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Size getBufferSize() {
+        return mBufferSize;
+    }
+
+    /**
+     * Whether the content captured will use filtering when scaling.
+     */
+    @DataClass.Generated.Member
+    public boolean isBufferScaleWithFiltering() {
+        return mBufferScaleWithFiltering;
+    }
+
+    /**
+     * Whether the content will be captured in grayscale or color.
+     */
+    @DataClass.Generated.Member
+    public boolean isGrayscaleBuffer() {
+        return mGrayscaleBuffer;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "DisplayHashParams { " +
+                "bufferSize = " + mBufferSize + ", " +
+                "bufferScaleWithFiltering = " + mBufferScaleWithFiltering + ", " +
+                "grayscaleBuffer = " + mGrayscaleBuffer +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mBufferScaleWithFiltering) flg |= 0x2;
+        if (mGrayscaleBuffer) flg |= 0x4;
+        if (mBufferSize != null) flg |= 0x1;
+        dest.writeByte(flg);
+        if (mBufferSize != null) dest.writeSize(mBufferSize);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ DisplayHashParams(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean bufferScaleWithFiltering = (flg & 0x2) != 0;
+        boolean grayscaleBuffer = (flg & 0x4) != 0;
+        Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
+
+        this.mBufferSize = bufferSize;
+        this.mBufferScaleWithFiltering = bufferScaleWithFiltering;
+        this.mGrayscaleBuffer = grayscaleBuffer;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DisplayHashParams> CREATOR
+            = new Parcelable.Creator<DisplayHashParams>() {
+        @Override
+        public DisplayHashParams[] newArray(int size) {
+            return new DisplayHashParams[size];
+        }
+
+        @Override
+        public DisplayHashParams createFromParcel(@NonNull android.os.Parcel in) {
+            return new DisplayHashParams(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1615565493989L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
+            inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final  boolean mBufferScaleWithFiltering\nprivate final  boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate  boolean mBufferScaleWithFiltering\nprivate  boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferScaleWithFiltering(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 331dbe9..2105d84 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -34,6 +34,8 @@
 import android.view.displayhash.DisplayHashResultCallback;
 import android.view.displayhash.VerifiedDisplayHash;
 
+import java.util.Map;
+
 /**
  * A service that handles generating and verify {@link DisplayHash}.
  *
@@ -50,15 +52,6 @@
             "android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
 
     /**
-     * Manifest metadata key for the resource string array containing the names of all hashing
-     * algorithms provided by the service.
-     *
-     * @hide
-     */
-    public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
-            "android.displayhash.available_algorithms";
-
-    /**
      * The {@link Intent} action that must be declared as handled by a service in its manifest
      * for the system to recognize it as a DisplayHash providing service.
      *
@@ -96,7 +89,7 @@
      * @param buffer        The buffer for the content to generate the hash for.
      * @param bounds        The size and position of the content in window space.
      * @param hashAlgorithm The String for the hashing algorithm to use based values in
-     *                      {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+     *                      {@link #getDisplayHashAlgorithms(RemoteCallback)}.
      * @param callback      The callback to invoke
      *                      {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)}
      *                      if successfully generated a DisplayHash or {@link
@@ -108,6 +101,12 @@
             @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
 
     /**
+     * Returns a map of supported algorithms and their {@link DisplayHashParams}
+     */
+    @NonNull
+    public abstract Map<String, DisplayHashParams> onGetDisplayHashAlgorithms();
+
+    /**
      * Call to verify that the DisplayHash passed in was generated by the system.
      *
      * @param salt        The salt value to use when verifying the hmac. This should be the
@@ -132,6 +131,15 @@
         callback.sendResult(data);
     }
 
+    private void getDisplayHashAlgorithms(RemoteCallback callback) {
+        Map<String, DisplayHashParams> displayHashParams = onGetDisplayHashAlgorithms();
+        final Bundle data = new Bundle();
+        for (Map.Entry<String, DisplayHashParams> entry : displayHashParams.entrySet()) {
+            data.putParcelable(entry.getKey(), entry.getValue());
+        }
+        callback.sendResult(data);
+    }
+
     private final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub {
         @Override
         public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
@@ -164,5 +172,11 @@
                     obtainMessage(DisplayHasherService::verifyDisplayHash,
                             DisplayHasherService.this, salt, displayHash, callback));
         }
+
+        @Override
+        public void getDisplayHashAlgorithms(RemoteCallback callback) {
+            mHandler.sendMessage(obtainMessage(DisplayHasherService::getDisplayHashAlgorithms,
+                    DisplayHasherService.this, callback));
+        }
     }
 }
diff --git a/core/java/android/service/displayhash/IDisplayHasherService.aidl b/core/java/android/service/displayhash/IDisplayHasherService.aidl
index 236bc28..d9dcdca 100644
--- a/core/java/android/service/displayhash/IDisplayHasherService.aidl
+++ b/core/java/android/service/displayhash/IDisplayHasherService.aidl
@@ -51,4 +51,11 @@
      * @param callback The callback invoked to send back the VerifiedDisplayHash.
      */
     void verifyDisplayHash(in byte[] salt, in DisplayHash displayHash, in RemoteCallback callback);
+
+    /**
+     * Call to get a map of supported algorithms and their {@link DisplayHashParams}
+     *
+     * @param callback The callback invoked to send back the map of algorithms to DisplayHashParams.
+     */
+    void getDisplayHashAlgorithms(in RemoteCallback callback);
 }
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 8e76e2f..8dec092 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -16,12 +16,15 @@
 
 package android.service.rotationresolver;
 
+import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.Surface;
 
+import com.android.internal.util.DataClass;
+
 /**
  * This class represents a request to an {@link RotationResolverService}. The request contains
  * information from the system that can help RotationResolverService to determine the appropriate
@@ -33,68 +36,209 @@
  * @hide
  */
 @SystemApi
+@DataClass (
+        genParcelable = true,
+        genToString = true
+)
 public final class RotationResolutionRequest implements Parcelable {
-    private final @NonNull String mPackageName;
-    private final int mProposedRotation;
-    private final int mCurrentRotation;
-    private final long mTimeoutMillis;
+    /** The Name of the package of the current fore-ground activity. */
+    @NonNull private final String mPackageName;
+
+    /** The current rotation of the screen. */
+    @Surface.Rotation private final int mCurrentRotation;
+
+    /** The proposed screen rotation in the system. */
+    @Surface.Rotation private final int mProposedRotation;
+
+    /** Whether should use camera signal to resolver rotation. */
+    private final boolean mShouldUseCamera;
+
+    /** The timeout of the request. */
+    @DurationMillisLong private final long mTimeoutMillis;
+
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
 
     /**
-     * @param proposedRotation The system proposed screen rotation.
-     * @param currentRotation  The current screen rotation of the phone.
-     * @param packageName The current package name of the activity that is running in
-     *                    foreground.
-     * @param timeoutMillis    The timeout in millisecond for the rotation request.
-     * @hide
+     * Creates a new RotationResolutionRequest.
+     *
+     * @param packageName
+     *   The Name of the package of the current fore-ground activity.
+     * @param currentRotation
+     *   The current rotation of the screen.
+     * @param proposedRotation
+     *   The proposed screen rotation in the system.
+     * @param shouldUseCamera
+     *   Whether should use camera signal to resolver rotation.
+     * @param timeoutMillis
+     *   The timeout of the request.
      */
-    public RotationResolutionRequest(int proposedRotation, int currentRotation,
-            @NonNull String packageName, long timeoutMillis) {
-        mProposedRotation = proposedRotation;
-        mCurrentRotation = currentRotation;
-        mPackageName = packageName;
-        mTimeoutMillis = timeoutMillis;
+    @DataClass.Generated.Member
+    public RotationResolutionRequest(
+            @NonNull String packageName,
+            @Surface.Rotation int currentRotation,
+            @Surface.Rotation int proposedRotation,
+            boolean shouldUseCamera,
+            @DurationMillisLong long timeoutMillis) {
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mCurrentRotation = currentRotation;
+        com.android.internal.util.AnnotationValidations.validate(
+                Surface.Rotation.class, null, mCurrentRotation);
+        this.mProposedRotation = proposedRotation;
+        com.android.internal.util.AnnotationValidations.validate(
+                Surface.Rotation.class, null, mProposedRotation);
+        this.mShouldUseCamera = shouldUseCamera;
+        this.mTimeoutMillis = timeoutMillis;
+        com.android.internal.util.AnnotationValidations.validate(
+                DurationMillisLong.class, null, mTimeoutMillis);
+
+        // onConstructed(); // You can define this method to get a callback
     }
 
-    @Surface.Rotation public int getProposedRotation() {
-        return mProposedRotation;
-    }
-
-    public int getCurrentRotation() {
-        return mCurrentRotation;
-    }
-
+    /**
+     * The Name of the package of the current fore-ground activity.
+     */
+    @DataClass.Generated.Member
     public @NonNull String getPackageName() {
         return mPackageName;
     }
 
-    public long getTimeoutMillis() {
+    /**
+     * The current rotation of the screen.
+     */
+    @DataClass.Generated.Member
+    public @Surface.Rotation int getCurrentRotation() {
+        return mCurrentRotation;
+    }
+
+    /**
+     * The proposed screen rotation in the system.
+     */
+    @DataClass.Generated.Member
+    public @Surface.Rotation int getProposedRotation() {
+        return mProposedRotation;
+    }
+
+    /**
+     * Whether should use camera signal to resolver rotation.
+     */
+    @DataClass.Generated.Member
+    public boolean shouldUseCamera() {
+        return mShouldUseCamera;
+    }
+
+    /**
+     * The timeout of the request.
+     */
+    @DataClass.Generated.Member
+    public @DurationMillisLong long getTimeoutMillis() {
         return mTimeoutMillis;
     }
 
     @Override
-    public int describeContents() {
-        return 0;
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "RotationResolutionRequest { " +
+                "packageName = " + mPackageName + ", " +
+                "currentRotation = " + mCurrentRotation + ", " +
+                "proposedRotation = " + mProposedRotation + ", " +
+                "shouldUseCamera = " + mShouldUseCamera + ", " +
+                "timeoutMillis = " + mTimeoutMillis +
+        " }";
     }
 
     @Override
-    public void writeToParcel(@NonNull Parcel parcel, int flags) {
-        parcel.writeInt(mProposedRotation);
-        parcel.writeInt(mCurrentRotation);
-        parcel.writeString(mPackageName);
-        parcel.writeLong(mTimeoutMillis);
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mShouldUseCamera) flg |= 0x8;
+        dest.writeByte(flg);
+        dest.writeString(mPackageName);
+        dest.writeInt(mCurrentRotation);
+        dest.writeInt(mProposedRotation);
+        dest.writeLong(mTimeoutMillis);
     }
 
-    public static final @NonNull Creator<RotationResolutionRequest> CREATOR =
-            new Creator<RotationResolutionRequest>() {
-        @Override
-        public RotationResolutionRequest createFromParcel(Parcel source) {
-            return new RotationResolutionRequest(source.readInt(), source.readInt(),
-                    source.readString(), source.readLong());
-        }
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
 
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RotationResolutionRequest(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean shouldUseCamera = (flg & 0x8) != 0;
+        String packageName = in.readString();
+        int currentRotation = in.readInt();
+        int proposedRotation = in.readInt();
+        long timeoutMillis = in.readLong();
+
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mCurrentRotation = currentRotation;
+        com.android.internal.util.AnnotationValidations.validate(
+                Surface.Rotation.class, null, mCurrentRotation);
+        this.mProposedRotation = proposedRotation;
+        com.android.internal.util.AnnotationValidations.validate(
+                Surface.Rotation.class, null, mProposedRotation);
+        this.mShouldUseCamera = shouldUseCamera;
+        this.mTimeoutMillis = timeoutMillis;
+        com.android.internal.util.AnnotationValidations.validate(
+                DurationMillisLong.class, null, mTimeoutMillis);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RotationResolutionRequest> CREATOR
+            = new Parcelable.Creator<RotationResolutionRequest>() {
         @Override
         public RotationResolutionRequest[] newArray(int size) {
             return new RotationResolutionRequest[size];
         }
+
+        @Override
+        public RotationResolutionRequest createFromParcel(@NonNull Parcel in) {
+            return new RotationResolutionRequest(in);
+        }
     };
+
+    @DataClass.Generated(
+            time = 1615402421314L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/service/rotationresolver/RotationResolutionRequest.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.view.Surface.Rotation int mCurrentRotation\nprivate final @android.view.Surface.Rotation int mProposedRotation\nprivate final  boolean mShouldUseCamera\nprivate final @android.annotation.DurationMillisLong long mTimeoutMillis\nclass RotationResolutionRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index d71a830..a9348c6 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -218,7 +218,30 @@
     }
 
     /**
-     * Starts the provider sending updates.
+     * Informs the provider that it should start detecting and reporting the detected time zone
+     * state via the various {@code report} methods. Implementations of {@link
+     * #onStartUpdates(long)} should return immediately, and will typically be used to start
+     * worker threads or begin asynchronous location listening.
+     *
+     * <p>Between {@link #onStartUpdates(long)} and {@link #onStopUpdates()} calls, the Android
+     * system server holds the latest report from the provider in memory. After an initial report,
+     * provider implementations are only required to send a report via {@link
+     * #reportSuggestion(TimeZoneProviderSuggestion)} or via {@link #reportUncertain()} when it
+     * differs from the previous report.
+     *
+     * <p>{@link #reportPermanentFailure(Throwable)} can also be called by provider implementations
+     * in rare cases, after which the provider should consider itself stopped and not make any
+     * further reports. {@link #onStopUpdates()} will not be called in this case.
+     *
+     * <p>The {@code initializationTimeoutMillis} parameter indicates how long the provider has been
+     * granted to call one of the {@code report} methods for the first time. If the provider does
+     * not call one of the {@code report} methods in this time, it may be judged uncertain and the
+     * Android system server may move on to use other providers or detection methods. Providers
+     * should therefore make best efforts during this time to generate a report, which could involve
+     * increased power usage. Providers should preferably report an explicit {@link
+     * #reportUncertain()} if the time zone(s) cannot be detected within the initialization timeout.
+     *
+     * @see #onStopUpdates() for the signal from the system server to stop sending reports
      */
     public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis);
 
@@ -228,7 +251,8 @@
     }
 
     /**
-     * Stops the provider sending updates.
+     * Stops the provider sending further updates. This will be called after {@link
+     * #onStartUpdates(long)}.
      */
     public abstract void onStopUpdates();
 
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index c1d9d58..def13db 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -41,10 +41,12 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.util.Slog;
 
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -287,6 +289,7 @@
     private final Handler mHandler;
     private final IBinder mBinder = new Binder();
     private final int mTargetSdkVersion;
+    private final boolean mSupportHotwordDetectionService;
 
     private int mAvailability = STATE_NOT_READY;
 
@@ -488,11 +491,22 @@
      * @param callback A non-null Callback for receiving the recognition events.
      * @param modelManagementService A service that allows management of sound models.
      * @param targetSdkVersion The target SDK version.
+     * @param supportHotwordDetectionService {@code true} if hotword detection service should be
+     * triggered, otherwise {@code false}.
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     *
      * @hide
      */
     public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
             KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
-            IVoiceInteractionManagerService modelManagementService, int targetSdkVersion) {
+            IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
+            boolean supportHotwordDetectionService, @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
         mText = text;
         mLocale = locale;
         mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
@@ -501,6 +515,10 @@
         mInternalCallback = new SoundTriggerListener(mHandler);
         mModelManagementService = modelManagementService;
         mTargetSdkVersion = targetSdkVersion;
+        mSupportHotwordDetectionService = supportHotwordDetectionService;
+        if (mSupportHotwordDetectionService) {
+            setHotwordDetectionServiceConfig(options, sharedMemory);
+        }
         try {
             Identity identity = new Identity();
             identity.packageName = ActivityThread.currentOpPackageName();
@@ -513,6 +531,38 @@
     }
 
     /**
+     * Set configuration and pass read-only data to hotword detection service.
+     *
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     *
+     * @throws IllegalStateException if it doesn't support hotword detection service.
+     *
+     * @hide
+     */
+    public final void setHotwordDetectionServiceConfig(@Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
+        if (DBG) {
+            Slog.d(TAG, "setHotwordDetectionServiceConfig()");
+        }
+        if (!mSupportHotwordDetectionService) {
+            throw new IllegalStateException(
+                    "setHotwordDetectionServiceConfig called, but it doesn't support hotword"
+                            + " detection service");
+        }
+
+        try {
+            mModelManagementService.setHotwordDetectionServiceConfig(options, sharedMemory);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the recognition modes supported by the associated keyphrase.
      *
      * @see #RECOGNITION_MODE_USER_IDENTIFICATION
@@ -839,6 +889,14 @@
         synchronized (mLock) {
             mAvailability = STATE_INVALID;
             notifyStateChangedLocked();
+
+            if (mSupportHotwordDetectionService) {
+                try {
+                    mModelManagementService.shutdownHotwordDetectionService();
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
         }
     }
 
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 7f1c5ff..fcef26f 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -27,13 +27,17 @@
 import android.app.Service;
 import android.content.Intent;
 import android.media.AudioFormat;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.util.Log;
 
+import java.util.Locale;
+
 /**
  * Implemented by an application that wants to offer detection for hotword. The system will
  * start the service after calling {@link VoiceInteractionService#setHotwordDetectionConfig}.
@@ -76,6 +80,17 @@
                     timeoutMillis,
                     new DspHotwordDetectionCallback(callback)));
         }
+
+        @Override
+        public void setConfig(Bundle options, SharedMemory sharedMemory) throws RemoteException {
+            if (DBG) {
+                Log.d(TAG, "#setConfig");
+            }
+            mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
+                    HotwordDetectionService.this,
+                    options,
+                    sharedMemory));
+        }
     };
 
     @CallSuper
@@ -121,6 +136,25 @@
     }
 
     /**
+     * Called when the {@link VoiceInteractionService#createAlwaysOnHotwordDetector(String, Locale,
+     * Bundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} or {@link AlwaysOnHotwordDetector#
+     * setHotwordDetectionServiceConfig(Bundle, SharedMemory)} requests an update of the hotword
+     * detection parameters.
+     *
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void onUpdateState(@Nullable Bundle options, @Nullable SharedMemory sharedMemory) {
+    }
+
+    /**
      * Callback for returning the detected result.
      *
      * @hide
diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl
index cbe76e4..8f0874a 100644
--- a/core/java/android/service/voice/IHotwordDetectionService.aidl
+++ b/core/java/android/service/voice/IHotwordDetectionService.aidl
@@ -17,7 +17,9 @@
 package android.service.voice;
 
 import android.media.AudioFormat;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
 import android.service.voice.IDspHotwordDetectionCallback;
 
 /**
@@ -31,4 +33,6 @@
     in AudioFormat audioFormat,
     long timeoutMillis,
     in IDspHotwordDetectionCallback callback);
+
+    void setConfig(in Bundle options, in SharedMemory sharedMemory);
 }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 25f8090..048d9f5 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -17,7 +17,6 @@
 package android.service.voice;
 
 import android.Manifest;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -36,6 +35,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SharedMemory;
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Log;
@@ -47,8 +47,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -73,32 +71,6 @@
     static final String TAG = VoiceInteractionService.class.getSimpleName();
 
     /**
-     * Indicates that the given configs have been set successfully after calling
-     * {@link VoiceInteractionService#setHotwordDetectionConfig}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int HOTWORD_CONFIG_SUCCESS = 0;
-
-    /**
-     * Indicates that the given configs have been set unsuccessfully after calling
-     * {@link VoiceInteractionService#setHotwordDetectionConfig}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int HOTWORD_CONFIG_FAILURE = 1;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = { "HOTWORD_CONFIG_" }, value = {
-            HOTWORD_CONFIG_SUCCESS,
-            HOTWORD_CONFIG_FAILURE,
-    })
-    public @interface HotwordConfigResult {}
-
-    /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
      * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
@@ -330,35 +302,6 @@
     }
 
     /**
-     * Set hotword detection configuration.
-     *
-     * Note: Currently it will trigger hotword detection service after calling this function when
-     * all conditions meet the requirements.
-     *
-     * @param options Config data.
-     * @return {@link VoiceInteractionService#HOTWORD_CONFIG_SUCCESS} in case of success,
-     * {@link VoiceInteractionService#HOTWORD_CONFIG_FAILURE} in case of failure.
-     *
-     * @throws IllegalStateException if the function is called before onReady() is called.
-     *
-     * @hide
-     */
-    @SystemApi
-    @HotwordConfigResult
-    public final int setHotwordDetectionConfig(
-            @SuppressLint("NullableCollection") @Nullable Bundle options) {
-        if (mSystemService == null) {
-            throw new IllegalStateException("Not available until onReady() is called");
-        }
-
-        try {
-            return mSystemService.setHotwordDetectionConfig(options);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
      * This instance must be retained and used by the client.
      * Calling this a second time invalidates the previously created hotword detector
@@ -374,9 +317,61 @@
     @SystemApi
     @NonNull
     public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
-            @SuppressLint("MissingNullability") String keyphrase,  // TODO: annotate nullability properly
+            @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
             @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
             @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+                /* supportHotwordDetectionService= */ false, /* options= */ null,
+                /* sharedMemory= */ null, callback);
+    }
+
+    /**
+     * Create an {@link AlwaysOnHotwordDetector} and trigger a {@link HotwordDetectionService}
+     * service, then it will also pass the read-only data to hotword detection service.
+     *
+     * Like {@see #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)
+     * }. Before calling this function, you should set a valid hotword detection service with
+     * android:hotwordDetectionService in an android.voice_interaction metadata file and set
+     * android:isolatedProcess="true" in the AndroidManifest.xml of hotword detection service.
+     * Otherwise it will throw IllegalStateException. After calling this function, the system will
+     * also trigger a hotword detection service and pass the read-only data back to it.
+     *
+     * <p>Note: The system will trigger hotword detection service after calling this function when
+     * all conditions meet the requirements.
+     *
+     * @param keyphrase The keyphrase that's being used, for example "Hello Android".
+     * @param locale The locale for which the enrollment needs to be performed.
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     * @param callback The callback to notify of detection events.
+     * @return An always-on hotword detector for the given keyphrase and locale.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
+            @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
+            @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+            @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory,
+            @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+                /* supportHotwordDetectionService= */ true, options,
+                sharedMemory, callback);
+    }
+
+    private AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorInternal(
+            @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
+            @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+            boolean supportHotwordDetectionService,
+            @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory,
+            @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
         if (mSystemService == null) {
             throw new IllegalStateException("Not available until onReady() is called");
         }
@@ -385,7 +380,8 @@
             safelyShutdownHotwordDetector();
             mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
                     mKeyphraseEnrollmentInfo, mSystemService,
-                    getApplicationContext().getApplicationInfo().targetSdkVersion);
+                    getApplicationContext().getApplicationInfo().targetSdkVersion,
+                    supportHotwordDetectionService, options, sharedMemory);
         }
         return mHotwordDetector;
     }
@@ -432,7 +428,6 @@
     }
 
     private void safelyShutdownHotwordDetector() {
-        // TODO (b/178171906): Need to check if the HotwordDetectionService should be unbound.
         synchronized (mLock) {
             if (mHotwordDetector == null) {
                 return;
diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl
index 8e5292d..ad40226 100644
--- a/core/java/android/speech/IRecognitionServiceManager.aidl
+++ b/core/java/android/speech/IRecognitionServiceManager.aidl
@@ -31,4 +31,6 @@
         in IBinder clientToken,
         boolean onDevice,
         in IRecognitionServiceManagerCallback callback);
+
+    void setTemporaryComponent(in ComponentName componentName);
 }
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 850f997..9b93a64 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -17,6 +17,8 @@
 package android.speech;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -35,6 +37,8 @@
 import android.util.Slog;
 
 import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
 
 /**
  * This class provides access to the speech recognition service. This service allows access to the
@@ -52,7 +56,7 @@
  */
 public class SpeechRecognizer {
     /** DEBUG value to enable verbose debug prints */
-    private final static boolean DBG = false;
+    private static final boolean DBG = false;
 
     /** Log messages identifier */
     private static final String TAG = "SpeechRecognizer";
@@ -113,10 +117,11 @@
     public static final int ERROR_SERVER_DISCONNECTED = 11;
 
     /** action codes */
-    private final static int MSG_START = 1;
-    private final static int MSG_STOP = 2;
-    private final static int MSG_CANCEL = 3;
-    private final static int MSG_CHANGE_LISTENER = 4;
+    private static final int MSG_START = 1;
+    private static final int MSG_STOP = 2;
+    private static final int MSG_CANCEL = 3;
+    private static final int MSG_CHANGE_LISTENER = 4;
+    private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;
 
     /** The actual RecognitionService endpoint */
     private IRecognitionService mService;
@@ -134,6 +139,7 @@
 
     /** Handler that will execute the main tasks */
     private Handler mHandler = new Handler(Looper.getMainLooper()) {
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -149,10 +155,19 @@
                 case MSG_CHANGE_LISTENER:
                     handleChangeListener((RecognitionListener) msg.obj);
                     break;
+                case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
+                    handleSetTemporaryComponent((ComponentName) msg.obj);
+                    break;
             }
         }
     };
 
+    /**
+     * Temporary queue, saving the messages until the connection will be established, afterwards,
+     * only mHandler will receive the messages
+     */
+    private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>();
+
     /** The Listener that will receive all the callbacks */
     private final InternalListener mListener = new InternalListener();
 
@@ -287,11 +302,9 @@
 
         if (mService == null) {
             // First time connection: first establish a connection, then dispatch #startListening.
-            connectToSystemService(
-                    () -> putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)));
-        } else {
-            putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
+            connectToSystemService();
         }
+        putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
     }
 
     /**
@@ -336,6 +349,22 @@
         putMessage(Message.obtain(mHandler, MSG_CANCEL));
     }
 
+    /**
+     * Sets a temporary component to power on-device speech recognizer.
+     *
+     * <p>This is only expected to be called in tests, system would reject calls from client apps.
+     *
+     * @param componentName name of the component to set temporary replace speech recognizer. {@code
+     *        null} value resets the recognizer to default.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setTemporaryOnDeviceRecognizer(@Nullable ComponentName componentName) {
+        mHandler.sendMessage(
+                Message.obtain(mHandler, MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT, componentName));
+    }
+
     private static void checkIsCalledFromMainThread() {
         if (Looper.myLooper() != Looper.getMainLooper()) {
             throw new RuntimeException(
@@ -344,7 +373,11 @@
     }
 
     private void putMessage(Message msg) {
-        mHandler.sendMessage(msg);
+        if (mService == null) {
+            mPendingTasks.offer(msg);
+        } else {
+            mHandler.sendMessage(msg);
+        }
     }
 
     /** sends the actual message to the service */
@@ -395,6 +428,22 @@
         }
     }
 
+    private void handleSetTemporaryComponent(ComponentName componentName) {
+        if (DBG) {
+            Log.d(TAG, "handleSetTemporaryComponent, componentName=" + componentName);
+        }
+
+        if (!maybeInitializeManagerService()) {
+            return;
+        }
+
+        try {
+            mManagerService.setTemporaryComponent(componentName);
+        } catch (final RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     private boolean checkOpenConnection() {
         if (mService != null) {
             return true;
@@ -422,16 +471,13 @@
         }
 
         mService = null;
+        mPendingTasks.clear();
         mListener.mInternalListener = null;
     }
 
     /** Establishes a connection to system server proxy and initializes the session. */
-    private void connectToSystemService(Runnable onSuccess) {
-        mManagerService = IRecognitionServiceManager.Stub.asInterface(
-                ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
-
-        if (mManagerService == null) {
-            mListener.onError(ERROR_CLIENT);
+    private void connectToSystemService() {
+        if (!maybeInitializeManagerService()) {
             return;
         }
 
@@ -450,13 +496,19 @@
                     new IRecognitionServiceManagerCallback.Stub(){
                         @Override
                         public void onSuccess(IRecognitionService service) throws RemoteException {
+                            if (DBG) {
+                                Log.i(TAG, "Connected to speech recognition service");
+                            }
                             mService = service;
-                            onSuccess.run();
+                            while (!mPendingTasks.isEmpty()) {
+                                mHandler.sendMessage(mPendingTasks.poll());
+                            }
                         }
 
                         @Override
                         public void onError(int errorCode) throws RemoteException {
-                            Log.e(TAG, "Bind to system recognition service failed");
+                            Log.e(TAG, "Bind to system recognition service failed with error "
+                                    + errorCode);
                             mListener.onError(errorCode);
                         }
                     });
@@ -465,6 +517,21 @@
         }
     }
 
+    private boolean maybeInitializeManagerService() {
+        if (mManagerService != null) {
+            return true;
+        }
+
+        mManagerService = IRecognitionServiceManager.Stub.asInterface(
+                ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
+
+        if (mManagerService == null && mListener != null) {
+            mListener.onError(ERROR_CLIENT);
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Returns the component name to be used for establishing a connection, based on the parameters
      * used during initialization.
@@ -505,15 +572,15 @@
     private static class InternalListener extends IRecognitionListener.Stub {
         private RecognitionListener mInternalListener;
 
-        private final static int MSG_BEGINNING_OF_SPEECH = 1;
-        private final static int MSG_BUFFER_RECEIVED = 2;
-        private final static int MSG_END_OF_SPEECH = 3;
-        private final static int MSG_ERROR = 4;
-        private final static int MSG_READY_FOR_SPEECH = 5;
-        private final static int MSG_RESULTS = 6;
-        private final static int MSG_PARTIAL_RESULTS = 7;
-        private final static int MSG_RMS_CHANGED = 8;
-        private final static int MSG_ON_EVENT = 9;
+        private static final int MSG_BEGINNING_OF_SPEECH = 1;
+        private static final int MSG_BUFFER_RECEIVED = 2;
+        private static final int MSG_END_OF_SPEECH = 3;
+        private static final int MSG_ERROR = 4;
+        private static final int MSG_READY_FOR_SPEECH = 5;
+        private static final int MSG_RESULTS = 6;
+        private static final int MSG_PARTIAL_RESULTS = 7;
+        private static final int MSG_RMS_CHANGED = 8;
+        private static final int MSG_ON_EVENT = 9;
 
         private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
             @Override
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index bbe887f..e9a79e7 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 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;
@@ -1577,7 +1576,7 @@
             // default implementation empty
         }
 
-        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
             // default implementation empty
         }
     }
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 2cadda2..e3d3dec 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -546,9 +546,6 @@
     /**
      * 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
      */
@@ -1265,30 +1262,34 @@
     public interface AllowedNetworkTypesListener {
         /**
          * Callback invoked when the current allowed network type list has changed on the
-         * registered subscription.
+         * registered subscription for a specified reason.
          * Note, the registered subscription is associated with {@link TelephonyManager} object
-         * on which
-         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * 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.
+         * @param reason an allowed network type reasons.
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+         *
+         * @param allowedNetworkType an allowed network type bitmask value. (for example,
+         * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}|
+         * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}})
+         *
          * 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}}
+         * If the latest allowed network type is changed by user, then the system
+         * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and
+         * long type value}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+        void onAllowedNetworkTypesChanged(
+                @TelephonyManager.AllowedNetworkTypesReason int reason,
+                @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
     }
 
     /**
@@ -1707,14 +1708,15 @@
                             enabled, reason)));
         }
 
-        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
             AllowedNetworkTypesListener listener =
                     (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
-                            () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+                            () -> listener.onAllowedNetworkTypesChanged(reason,
+                                    allowedNetworkType)));
         }
     }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9cda4ae..3fa63d8 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -825,16 +825,18 @@
     }
 
     /**
-     * Notify emergency number list changed on certain subscription.
-     *
-     * @param slotIndex for which emergency number list changed. Can be derived from subId except
-     * when subId is invalid.
-     * @param subId for which emergency number list changed.
+     * Notify the allowed network types has changed for a specific subscription and the specific
+     * reason.
+     * @param slotIndex for which allowed network types changed.
+     * @param subId for which allowed network types changed.
+     * @param reason an allowed network type reasons.
+     * @param allowedNetworkType an allowed network type bitmask value.
      */
     public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
-            Map<Integer, Long> allowedNetworkTypeList) {
+            int reason, long allowedNetworkType) {
         try {
-            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList);
+            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason,
+                    allowedNetworkType);
         } catch (RemoteException ex) {
             // system process is dead
         }
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 3880131..f61ab29 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,14 +16,26 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Formatter;
+import java.util.Locale;
+
 /**
  * @hide
  */
 public final class Slog {
 
+    @GuardedBy("sMessageBuilder")
+    private static final StringBuilder sMessageBuilder = new StringBuilder();
+
+    @GuardedBy("sMessageBuilder")
+    private static final Formatter sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH);
+
     private Slog() {
     }
 
@@ -37,6 +49,15 @@
                 msg + '\n' + Log.getStackTraceString(tr));
     }
 
+    /**
+     * Logs a {@link Log.VERBOSE} message.
+     */
+    public static void v(String tag, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.VERBOSE)) return;
+
+        v(tag, getMessage(format, args));
+    }
+
     @UnsupportedAppUsage
     public static int d(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
@@ -48,6 +69,15 @@
                 msg + '\n' + Log.getStackTraceString(tr));
     }
 
+    /**
+     * Logs a {@link Log.DEBUG} message.
+     */
+    public static void d(String tag, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.DEBUG)) return;
+
+        d(tag, getMessage(format, args));
+    }
+
     @UnsupportedAppUsage
     public static int i(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
@@ -58,6 +88,15 @@
                 msg + '\n' + Log.getStackTraceString(tr));
     }
 
+    /**
+     * Logs a {@link Log.INFO} message.
+     */
+    public static void i(String tag, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.INFO)) return;
+
+        i(tag, getMessage(format, args));
+    }
+
     @UnsupportedAppUsage
     public static int w(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
@@ -73,6 +112,24 @@
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
     }
 
+    /**
+     * Logs a {@link Log.WARN} message.
+     */
+    public static void w(String tag, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.WARN)) return;
+
+        w(tag, getMessage(format, args));
+    }
+
+    /**
+     * Logs a {@link Log.WARN} message with an exception
+     */
+    public static void w(String tag, Exception exception, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.WARN)) return;
+
+        w(tag, getMessage(format, args), exception);
+    }
+
     @UnsupportedAppUsage
     public static int e(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
@@ -85,6 +142,24 @@
     }
 
     /**
+     * Logs a {@link Log.ERROR} message.
+     */
+    public static void e(String tag, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+        e(tag, getMessage(format, args));
+    }
+
+    /**
+     * Logs a {@link Log.ERROR} message with an exception
+     */
+    public static void e(String tag, Exception exception, String format, @Nullable Object... args) {
+        if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+        e(tag, getMessage(format, args), exception);
+    }
+
+    /**
      * Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
      * will always be handled asynchronously.  Primarily for use by coding running within
      * the system process.
@@ -95,6 +170,21 @@
     }
 
     /**
+     * Logs a {@code wtf} message.
+     */
+    public static void wtf(String tag, String format, @Nullable Object... args) {
+        wtf(tag, getMessage(format, args));
+    }
+
+    /**
+     * Logs a {@code wtf} message with an exception.
+     */
+    public static void wtf(String tag, Exception exception, String format,
+            @Nullable Object... args) {
+        wtf(tag, getMessage(format, args), exception);
+    }
+
+    /**
      * Like {@link #wtf(String, String)}, but does not output anything to the log.
      */
     public static void wtfQuiet(String tag, String msg) {
@@ -134,5 +224,13 @@
     public static int println(int priority, String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
     }
-}
 
+    private static String getMessage(String format, @Nullable Object... args) {
+        synchronized (sMessageBuilder) {
+            sFormatter.format(format, args);
+            String message = sMessageBuilder.toString();
+            sMessageBuilder.setLength(0);
+            return message;
+        }
+    }
+}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index c97c995..7e6175c 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -200,10 +200,9 @@
         // physical memory.
 
         DataSource beforeApkSigningBlock =
-                new MemoryMappedFileDataSource(apkFileDescriptor, 0,
-                        signatureInfo.apkSigningBlockOffset);
+                DataSource.create(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset);
         DataSource centralDir =
-                new MemoryMappedFileDataSource(
+                DataSource.create(
                         apkFileDescriptor, signatureInfo.centralDirOffset,
                         signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
 
diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java
index 82f3800..dd6389d 100644
--- a/core/java/android/util/apk/DataSource.java
+++ b/core/java/android/util/apk/DataSource.java
@@ -16,6 +16,10 @@
 
 package android.util.apk;
 
+import android.annotation.NonNull;
+import android.os.incremental.IncrementalManager;
+
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.security.DigestException;
 
@@ -35,4 +39,22 @@
      */
     void feedIntoDataDigester(DataDigester md, long offset, int size)
             throws IOException, DigestException;
+
+    /**
+     * Creates a DataSource that can handle the passed fd in the most efficient and safe manner.
+     * @param fd file descriptor to read from
+     * @param pos starting offset
+     * @param size size of the region
+     * @return created DataSource object
+     */
+    static @NonNull DataSource create(@NonNull FileDescriptor fd, long pos, long size) {
+        if (IncrementalManager.isIncrementalFileFd(fd)) {
+            // IncFS-based files may have missing pages, and reading those via mmap() results
+            // in a SIGBUS signal. Java doesn't have a good way of catching it, ending up killing
+            // the process by default. Going back to read() is the safest option for these files.
+            return new ReadFileDataSource(fd, pos, size);
+        } else {
+            return new MemoryMappedFileDataSource(fd, pos, size);
+        }
+    }
 }
diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java
index 8d2b1e32..69a526d 100644
--- a/core/java/android/util/apk/MemoryMappedFileDataSource.java
+++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java
@@ -40,6 +40,7 @@
     /**
      * Constructs a new {@code MemoryMappedFileDataSource} for the specified region of the file.
      *
+     * @param fd file descriptor to read from.
      * @param position start position of the region in the file.
      * @param size size (in bytes) of the region.
      */
diff --git a/core/java/android/util/apk/ReadFileDataSource.java b/core/java/android/util/apk/ReadFileDataSource.java
new file mode 100644
index 0000000..d0e1140
--- /dev/null
+++ b/core/java/android/util/apk/ReadFileDataSource.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.util.apk;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+
+/**
+ * {@link DataSource} which provides data from a file descriptor by reading the sections
+ * of the file via raw read() syscall. This is slower than memory-mapping but safer.
+ */
+class ReadFileDataSource implements DataSource {
+    private final FileDescriptor mFd;
+    private final long mFilePosition;
+    private final long mSize;
+
+    private static final int CHUNK_SIZE = 1024 * 1024;
+
+    /**
+     * Constructs a new {@code ReadFileDataSource} for the specified region of the file.
+     *
+     * @param fd file descriptor to read from.
+     * @param position start position of the region in the file.
+     * @param size size (in bytes) of the region.
+     */
+    ReadFileDataSource(FileDescriptor fd, long position, long size) {
+        mFd = fd;
+        mFilePosition = position;
+        mSize = size;
+    }
+
+    @Override
+    public long size() {
+        return mSize;
+    }
+
+    @Override
+    public void feedIntoDataDigester(DataDigester md, long offset, int size)
+            throws IOException, DigestException {
+        try {
+            final byte[] buffer = new byte[Math.min(size, CHUNK_SIZE)];
+            final long start = mFilePosition + offset;
+            final long end = start + size;
+            for (long pos = start, curSize = Math.min(size, CHUNK_SIZE);
+                    pos < end; curSize = Math.min(end - pos, CHUNK_SIZE)) {
+                final int readSize = Os.pread(mFd, buffer, 0, (int) curSize, pos);
+                md.consume(ByteBuffer.wrap(buffer, 0, readSize));
+                pos += readSize;
+            }
+        } catch (ErrnoException e) {
+            throw new IOException(e);
+        }
+    }
+}
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
index 8544e82..4598b4f 100644
--- a/core/java/android/util/apk/TEST_MAPPING
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -1,6 +1,17 @@
 {
   "presubmit": [
     {
+      "name": "CtsContentTestCases",
+      "options": [
+        {
+          "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+        },
+        {
+          "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+        }
+      ]
+    },
+    {
       "name": "FrameworksCoreTests",
       "options": [
         {
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index 4596c6e..b0a5992 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -294,7 +294,7 @@
 
         // 1. Digest the whole file by chunks.
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(file.getFD(), 0, file.length()),
+                DataSource.create(file.getFD(), 0, file.length()),
                 MMAP_REGION_SIZE_BYTES);
 
         // 2. Pad 0s up to the nearest 4096-byte block before hashing.
@@ -315,7 +315,7 @@
 
         // 1. Digest from the beginning of the file, until APK Signing Block is reached.
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
+                DataSource.create(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
                 MMAP_REGION_SIZE_BYTES);
 
         // 2. Skip APK Signing Block and continue digesting, until the Central Directory offset
@@ -323,7 +323,7 @@
         long eocdCdOffsetFieldPosition =
                 signatureInfo.eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET;
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(apk.getFD(), signatureInfo.centralDirOffset,
+                DataSource.create(apk.getFD(), signatureInfo.centralDirOffset,
                     eocdCdOffsetFieldPosition - signatureInfo.centralDirOffset),
                 MMAP_REGION_SIZE_BYTES);
 
@@ -338,7 +338,7 @@
         long offsetAfterEocdCdOffsetField =
                 eocdCdOffsetFieldPosition + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(apk.getFD(), offsetAfterEocdCdOffsetField,
+                DataSource.create(apk.getFD(), offsetAfterEocdCdOffsetField,
                     apk.length() - offsetAfterEocdCdOffsetField),
                 MMAP_REGION_SIZE_BYTES);
 
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
index 49ff237..b28cfb8 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -23,7 +23,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodManager;
@@ -104,12 +103,6 @@
     public abstract void addToBuffer(ProtoOutputStream proto, int source);
 
     /**
-     * @param shell The shell command to process
-     * @return {@code 0} if the command was successfully processed, {@code -1} otherwise
-     */
-    public abstract int onShellCommand(ShellCommand shell);
-
-    /**
      * Starts a proto dump of the client side information.
      *
      * @param where Place where the trace was triggered.
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
index 2c27639..35a81b7 100644
--- a/core/java/android/util/imetracing/ImeTracingClientImpl.java
+++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java
@@ -20,7 +20,6 @@
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.RemoteException;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodManager;
@@ -45,11 +44,6 @@
     }
 
     @Override
-    public int onShellCommand(ShellCommand shell) {
-        return -1;
-    }
-
-    @Override
     public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
             ProtoOutputStream icProto) {
         if (!isEnabled() || !isAvailable()) {
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
index e793c28..77f017a 100644
--- a/core/java/android/util/imetracing/ImeTracingServerImpl.java
+++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java
@@ -22,7 +22,6 @@
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.RemoteException;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
@@ -106,32 +105,6 @@
         }
     }
 
-    /**
-     * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>"
-     *
-     * @param shell The shell command to process
-     * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise
-     */
-    @Override
-    public int onShellCommand(ShellCommand shell) {
-        PrintWriter pw = shell.getOutPrintWriter();
-        String cmd = shell.getNextArgRequired();
-        switch (cmd) {
-            case "start":
-                startTrace(pw);
-                return 0;
-            case "stop":
-                stopTrace(pw);
-                return 0;
-            default:
-                pw.println("Unknown command: " + cmd);
-                pw.println("Input method trace options:");
-                pw.println("  start: Start tracing");
-                pw.println("  stop: Stop tracing");
-                return -1;
-        }
-    }
-
     @Override
     public void triggerClientDump(String where, InputMethodManager immInstance,
             ProtoOutputStream icProto) {
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 9df213b..8c771ba 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -38,9 +38,30 @@
     private final double mErrorRadians;
     private final double mConfidenceLevel;
 
-    private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+    /**
+     * Constructs a new {@link AngleMeasurement} object
+     *
+     * @param radians the angle in radians
+     * @param errorRadians the error of the angle measurement in radians
+     * @param confidenceLevel confidence level of the angle measurement
+     *
+     * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
+     *                                  allowed range
+     */
+    public AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+        if (radians < -Math.PI || radians > Math.PI) {
+            throw new IllegalArgumentException("Invalid radians: " + radians);
+        }
         mRadians = radians;
+
+        if (errorRadians < 0.0 || errorRadians > Math.PI) {
+            throw new IllegalArgumentException("Invalid error radians: " + errorRadians);
+        }
         mErrorRadians = errorRadians;
+
+        if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+            throw new IllegalArgumentException("Invalid confidence level: " + confidenceLevel);
+        }
         mConfidenceLevel = confidenceLevel;
     }
 
@@ -122,11 +143,7 @@
             new Creator<AngleMeasurement>() {
                 @Override
                 public AngleMeasurement createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-                    builder.setRadians(in.readDouble());
-                    builder.setErrorRadians(in.readDouble());
-                    builder.setConfidenceLevel(in.readDouble());
-                    return builder.build();
+                    return new AngleMeasurement(in.readDouble(), in.readDouble(), in.readDouble());
                 }
 
                 @Override
@@ -134,82 +151,4 @@
                     return new AngleMeasurement[size];
                 }
     };
-
-    /**
-     * Builder class for {@link AngleMeasurement}.
-     */
-    public static final class Builder {
-        private double mRadians = Double.NaN;
-        private double mErrorRadians = Double.NaN;
-        private double mConfidenceLevel = Double.NaN;
-
-        /**
-         * Set the angle in radians
-         *
-         * @param radians angle in radians
-         * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
-         */
-        @NonNull
-        public Builder setRadians(double radians) {
-            if (radians < -Math.PI || radians > Math.PI) {
-                throw new IllegalArgumentException("Invalid radians: " + radians);
-            }
-            mRadians = radians;
-            return this;
-        }
-
-        /**
-         * Set the angle error in radians
-         *
-         * @param errorRadians error of the angle in radians
-         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
-         */
-        @NonNull
-        public Builder setErrorRadians(double errorRadians) {
-            if (errorRadians < 0.0 || errorRadians > Math.PI) {
-                throw new IllegalArgumentException(
-                        "Invalid error radians: " + errorRadians);
-            }
-            mErrorRadians = errorRadians;
-            return this;
-        }
-
-        /**
-         * Set the angle confidence level
-         *
-         * @param confidenceLevel level of confidence of the angle measurement
-         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
-         */
-        @NonNull
-        public Builder setConfidenceLevel(double confidenceLevel) {
-            if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
-                throw new IllegalArgumentException(
-                        "Invalid confidence level: " + confidenceLevel);
-            }
-            mConfidenceLevel = confidenceLevel;
-            return this;
-        }
-
-        /**
-         * Build the {@link AngleMeasurement} object
-         *
-         * @throws IllegalStateException if angle, error, or confidence values are missing
-         */
-        @NonNull
-        public AngleMeasurement build() {
-            if (Double.isNaN(mRadians)) {
-                throw new IllegalStateException("Angle is not set");
-            }
-
-            if (Double.isNaN(mErrorRadians)) {
-                throw new IllegalStateException("Angle error is not set");
-            }
-
-            if (Double.isNaN(mConfidenceLevel)) {
-                throw new IllegalStateException("Angle confidence level is not set");
-            }
-
-            return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel);
-        }
-    }
 }
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 3d8626b..db04ad1 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -116,9 +116,8 @@
             new Creator<AngleOfArrivalMeasurement>() {
                 @Override
                 public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-
-                    builder.setAzimuth(in.readParcelable(AngleMeasurement.class.getClassLoader()));
+                    Builder builder =
+                            new Builder(in.readParcelable(AngleMeasurement.class.getClassLoader()));
 
                     builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
 
@@ -135,18 +134,16 @@
      * Builder class for {@link AngleOfArrivalMeasurement}.
      */
     public static final class Builder {
-        private AngleMeasurement mAzimuthAngleMeasurement = null;
+        private final AngleMeasurement mAzimuthAngleMeasurement;
         private AngleMeasurement mAltitudeAngleMeasurement = null;
 
         /**
-         * Set the azimuth angle
+         * Constructs an {@link AngleOfArrivalMeasurement} object
          *
-         * @param azimuthAngle azimuth angle
+         * @param azimuthAngle the azimuth angle of the measurement
          */
-        @NonNull
-        public Builder setAzimuth(@NonNull AngleMeasurement azimuthAngle) {
+        public Builder(@NonNull AngleMeasurement azimuthAngle) {
             mAzimuthAngleMeasurement = azimuthAngle;
-            return this;
         }
 
         /**
@@ -162,15 +159,9 @@
 
         /**
          * Build the {@link AngleOfArrivalMeasurement} object
-         *
-         * @throws IllegalStateException if the required azimuth angle is not provided
          */
         @NonNull
         public AngleOfArrivalMeasurement build() {
-            if (mAzimuthAngleMeasurement == null) {
-                throw new IllegalStateException("Azimuth angle measurement is not set");
-            }
-
             return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
                     mAltitudeAngleMeasurement);
         }
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 468a69c..4036892 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -62,9 +62,6 @@
   /**
    * Request to open a new ranging session
    *
-   * This function must return before calling any functions in
-   * IUwbAdapterCallbacks.
-   *
    * This function does not start the ranging session, but all necessary
    * components must be initialized and ready to start a new ranging
    * session prior to calling IUwbAdapterCallback#onRangingOpened.
@@ -77,12 +74,16 @@
    * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
    * if the ranging session fails to be opened.
    *
+   * If the provided sessionHandle is already open for the calling client, then
+   * #onRangingOpenFailed must be called and the new session must not be opened.
+   *
+   * @param sessionHandle the session handle to open ranging for
    * @param rangingCallbacks the callbacks used to deliver ranging information
    * @param parameters the configuration to use for ranging
-   * @return a SessionHandle used to identify this ranging request
    */
-  SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks,
-                            in PersistableBundle parameters);
+  void openRanging(in SessionHandle sessionHandle,
+                   in IUwbRangingCallbacks rangingCallbacks,
+                   in PersistableBundle parameters);
 
   /**
    * Request to start ranging
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index c0d8187..85f2c1c 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -17,6 +17,7 @@
 package android.uwb;
 
 import android.annotation.NonNull;
+import android.os.CancellationSignal;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.util.Log;
@@ -32,6 +33,7 @@
 
     private final IUwbAdapter mAdapter;
     private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
+    private int mNextSessionId = 1;
 
     public RangingManager(IUwbAdapter adapter) {
         mAdapter = adapter;
@@ -44,29 +46,26 @@
      * @param executor {@link Executor} to run callbacks
      * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
      *                  that is being opened.
-     * @return a new {@link RangingSession}
+     * @return a {@link CancellationSignal} that may be used to cancel the opening of the
+     *         {@link RangingSession}.
      */
-    public RangingSession openSession(@NonNull PersistableBundle params, @NonNull Executor executor,
+    public CancellationSignal openSession(@NonNull PersistableBundle params,
+            @NonNull Executor executor,
             @NonNull RangingSession.Callback callbacks) {
-        SessionHandle sessionHandle;
-        try {
-            sessionHandle = mAdapter.openRanging(this, params);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
         synchronized (this) {
-            if (hasSession(sessionHandle)) {
-                Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle");
-                executor.execute(() -> callbacks.onClosed(
-                        RangingSession.Callback.REASON_GENERIC_ERROR,
-                        new PersistableBundle()));
-            }
-
+            SessionHandle sessionHandle = new SessionHandle(mNextSessionId++);
             RangingSession session =
                     new RangingSession(executor, callbacks, mAdapter, sessionHandle);
             mRangingSessionTable.put(sessionHandle, session);
-            return session;
+            try {
+                mAdapter.openRanging(sessionHandle, this, params);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            cancellationSignal.setOnCancelListener(() -> session.close());
+            return cancellationSignal;
         }
     }
 
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 63a6d05..844bbbe 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -228,14 +229,14 @@
      * @param callbacks {@link RangingSession.Callback} to associate with the
      *                  {@link RangingSession} that is being opened.
      *
-     * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
+     * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a
      *         {@link RangingSession} that has been requested through {@link #openRangingSession}
      *         but has not yet been made available by
      *         {@link RangingSession.Callback#onOpened(RangingSession)}.
      */
     @NonNull
     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
+    public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RangingSession.Callback callbacks) {
         return mRangingManager.openSession(parameters, executor, callbacks);
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index 5a1b850..55fc4f4 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -16,13 +16,19 @@
 
 package android.view;
 
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.util.ArraySet;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -42,7 +48,7 @@
     private static final Object sLock = new Object();
 
     private final BlurEnabledListenerInternal mListenerInternal = new BlurEnabledListenerInternal();
-    private final ArraySet<Consumer<Boolean>> mListeners = new ArraySet();
+    private final ArrayMap<Consumer<Boolean>, Executor> mListeners = new ArrayMap();
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
     private boolean mInternalListenerAttached = false;
     private boolean mCrossWindowBlurEnabled;
@@ -74,20 +80,22 @@
         }
     }
 
-    void addListener(Consumer<Boolean> listener) {
-        if (listener == null) return;
+    void addListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> listener) {
+        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Preconditions.checkNotNull(executor, "executor cannot be null");
 
         synchronized (sLock) {
             attachInternalListenerIfNeededLocked();
 
-            mListeners.add(listener);
-            notifyListenerOnMain(listener, mCrossWindowBlurEnabled);
+            mListeners.put(listener, executor);
+            notifyListener(listener, executor, mCrossWindowBlurEnabled);
         }
     }
 
 
     void removeListener(Consumer<Boolean> listener) {
-        if (listener == null) return;
+        Preconditions.checkNotNull(listener, "listener cannot be null");
 
         synchronized (sLock) {
             mListeners.remove(listener);
@@ -116,10 +124,8 @@
         }
     }
 
-    private void notifyListenerOnMain(Consumer<Boolean> listener, boolean enabled) {
-        mMainHandler.post(() -> {
-            listener.accept(enabled);
-        });
+    private void notifyListener(Consumer<Boolean> listener, Executor executor, boolean enabled) {
+        executor.execute(() -> listener.accept(enabled));
     }
 
     private final class BlurEnabledListenerInternal extends ICrossWindowBlurEnabledListener.Stub {
@@ -128,8 +134,13 @@
             synchronized (sLock) {
                 mCrossWindowBlurEnabled = enabled;
 
-                for (int i = 0; i < mListeners.size(); i++) {
-                    notifyListenerOnMain(mListeners.valueAt(i), enabled);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    for (int i = 0; i < mListeners.size(); i++) {
+                        notifyListener(mListeners.keyAt(i), mListeners.valueAt(i), enabled);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(token);
                 }
             }
         }
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index b98ef14..d484f4d4 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -84,6 +84,7 @@
     private static final String TAG = "Display";
     private static final boolean DEBUG = false;
 
+    private final Object mLock = new Object();
     private final DisplayManagerGlobal mGlobal;
     private final int mDisplayId;
     private final int mFlags;
@@ -569,7 +570,7 @@
      * @return True if the display is still valid.
      */
     public boolean isValid() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mIsValid;
         }
@@ -584,7 +585,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public boolean getDisplayInfo(DisplayInfo outDisplayInfo) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             outDisplayInfo.copyFrom(mDisplayInfo);
             return mIsValid;
@@ -601,7 +602,7 @@
      * @hide
      */
     public int getLayerStack() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.layerStack;
         }
@@ -648,7 +649,7 @@
      * @hide
      */
     public DisplayAddress getAddress() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.address;
         }
@@ -708,7 +709,7 @@
      * @return The display's name.
      */
     public String getName() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.name;
         }
@@ -721,7 +722,7 @@
      * @hide
      */
     public float getBrightnessDefault() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.brightnessDefault;
         }
@@ -760,7 +761,7 @@
      */
     @Deprecated
     public void getSize(Point outSize) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
             outSize.x = mTempMetrics.widthPixels;
@@ -777,7 +778,7 @@
      */
     @Deprecated
     public void getRectSize(Rect outSize) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
             outSize.set(0, 0, mTempMetrics.widthPixels, mTempMetrics.heightPixels);
@@ -815,7 +816,7 @@
      * for example, screen decorations like the status bar are being hidden.
      */
     public void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             outSmallestSize.x = mDisplayInfo.smallestNominalAppWidth;
             outSmallestSize.y = mDisplayInfo.smallestNominalAppHeight;
@@ -831,7 +832,7 @@
      */
     @UnsupportedAppUsage
     public int getMaximumSizeDimension() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return Math.max(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
         }
@@ -842,7 +843,7 @@
      */
     @Deprecated
     public int getWidth() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateCachedAppSizeIfNeededLocked();
             return mCachedAppWidthCompat;
         }
@@ -853,7 +854,7 @@
      */
     @Deprecated
     public int getHeight() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateCachedAppSizeIfNeededLocked();
             return mCachedAppHeightCompat;
         }
@@ -878,7 +879,7 @@
      */
     @Surface.Rotation
     public int getRotation() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mMayAdjustByFixedRotation
                     ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
@@ -904,7 +905,7 @@
      */
     @Nullable
     public DisplayCutout getCutout() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mMayAdjustByFixedRotation
                     ? getDisplayAdjustments().getDisplayCutout(mDisplayInfo.displayCutout)
@@ -922,7 +923,7 @@
     @SuppressLint("VisiblySynchronized")
     @Nullable
     public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             RoundedCorners roundedCorners;
             if (mMayAdjustByFixedRotation) {
@@ -954,7 +955,7 @@
      * Gets the refresh rate of this display in frames per second.
      */
     public float getRefreshRate() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.getRefreshRate();
         }
@@ -970,7 +971,7 @@
      */
     @Deprecated
     public float[] getSupportedRefreshRates() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.getDefaultRefreshRates();
         }
@@ -980,7 +981,7 @@
      * Returns the active mode of the display.
      */
     public Mode getMode() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.getMode();
         }
@@ -990,7 +991,7 @@
      * Gets the supported modes of this display.
      */
     public Mode[] getSupportedModes() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             final Display.Mode[] modes = mDisplayInfo.supportedModes;
             return Arrays.copyOf(modes, modes.length);
@@ -1016,7 +1017,7 @@
      */
     @SuppressLint("VisiblySynchronized")
     public boolean isMinimalPostProcessingSupported() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.minimalPostProcessingSupported;
         }
@@ -1036,7 +1037,7 @@
      * @hide
      */
     public int getColorMode() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.colorMode;
         }
@@ -1063,7 +1064,7 @@
      * @see #isHdr()
      */
     public HdrCapabilities getHdrCapabilities() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.hdrCapabilities;
         }
@@ -1076,7 +1077,7 @@
      * @see HdrCapabilities#getSupportedHdrTypes()
      */
     public boolean isHdr() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.isHdr();
         }
@@ -1089,7 +1090,7 @@
      * {@link Configuration#isScreenWideColorGamut()}.
      */
     public boolean isWideColorGamut() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.isWideColorGamut();
         }
@@ -1104,7 +1105,7 @@
      */
     @Nullable
     public ColorSpace getPreferredWideGamutColorSpace() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             if (mDisplayInfo.isWideColorGamut()) {
                 return mGlobal.getPreferredWideGamutColorSpace();
@@ -1118,7 +1119,7 @@
      * @hide
      */
     public int[] getSupportedColorModes() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             int[] colorModes = mDisplayInfo.supportedColorModes;
             return Arrays.copyOf(colorModes, colorModes.length);
@@ -1135,7 +1136,7 @@
     @NonNull
     @TestApi
     public @ColorMode ColorSpace[] getSupportedWideColorGamut() {
-        synchronized (this) {
+        synchronized (mLock) {
             final ColorSpace[] defaultColorSpaces = new ColorSpace[0];
             updateDisplayInfoLocked();
             if (!isWideColorGamut()) {
@@ -1169,7 +1170,7 @@
      * A/V synchronization.
      */
     public long getAppVsyncOffsetNanos() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.appVsyncOffsetNanos;
         }
@@ -1187,7 +1188,7 @@
      * ({@link System#nanoTime}).
      */
     public long getPresentationDeadlineNanos() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mDisplayInfo.presentationDeadlineNanos;
         }
@@ -1202,7 +1203,10 @@
      */
     @Nullable
     public DeviceProductInfo getDeviceProductInfo() {
-        return mDisplayInfo.deviceProductInfo;
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.deviceProductInfo;
+        }
     }
 
     /**
@@ -1235,7 +1239,7 @@
      */
     @Deprecated
     public void getMetrics(DisplayMetrics outMetrics) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             mDisplayInfo.getAppMetrics(outMetrics, getDisplayAdjustments());
         }
@@ -1288,7 +1292,7 @@
      */
     @Deprecated
     public void getRealSize(Point outSize) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             if (shouldReportMaxBounds()) {
                 final Rect bounds = mResources.getConfiguration()
@@ -1358,7 +1362,7 @@
      */
     @Deprecated
     public void getRealMetrics(DisplayMetrics outMetrics) {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             if (shouldReportMaxBounds()) {
                 mDisplayInfo.getMaxBoundsMetrics(outMetrics,
@@ -1434,7 +1438,7 @@
      * {@link #STATE_UNKNOWN}.
      */
     public int getState() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             return mIsValid ? mDisplayInfo.state : STATE_UNKNOWN;
         }
@@ -1518,7 +1522,7 @@
     // For debugging purposes
     @Override
     public String toString() {
-        synchronized (this) {
+        synchronized (mLock) {
             updateDisplayInfoLocked();
             final DisplayAdjustments adjustments = getDisplayAdjustments();
             mDisplayInfo.getAppMetrics(mTempMetrics, adjustments);
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index a8aaeb7..9aaf5c0 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -636,7 +636,9 @@
     public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
             Configuration configuration) {
         Rect bounds = configuration.windowConfiguration.getMaxBounds();
-        getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
+        // Pass in null configuration to ensure width and height are not overridden to app bounds.
+        getMetricsWithSize(outMetrics, compatInfo, /* configuration= */ null,
+                bounds.width(), bounds.height());
     }
 
     public int getNaturalWidth() {
diff --git a/core/java/android/view/DragAndDropPermissions.java b/core/java/android/view/DragAndDropPermissions.java
index d47604d..16204d8 100644
--- a/core/java/android/view/DragAndDropPermissions.java
+++ b/core/java/android/view/DragAndDropPermissions.java
@@ -16,12 +16,15 @@
 
 package android.view;
 
+import static java.lang.Integer.toHexString;
+
 import android.app.Activity;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.view.IDragAndDropPermissions;
 
@@ -56,9 +59,28 @@
  */
 public final class DragAndDropPermissions implements Parcelable {
 
-    private final IDragAndDropPermissions mDragAndDropPermissions;
+    private static final String TAG = "DragAndDrop";
+    private static final boolean DEBUG = false;
 
-    private IBinder mTransientToken;
+    /**
+     * Permissions for a drop can be granted in one of two ways:
+     * <ol>
+     *     <li>An app can explicitly request permissions using
+     *     {@link Activity#requestDragAndDropPermissions(DragEvent)}. In this case permissions are
+     *     revoked automatically when then activity is destroyed. See {@link #take(IBinder)}.
+     *     <li>The platform can request permissions on behalf of the app (e.g. in
+     *     {@link android.widget.Editor}). In this case permissions are revoked automatically when
+     *     the app process terminates. See {@link #takeTransient()}.
+     * </ol>
+     *
+     * <p>In order to implement the second case above, we create a static token object here. This
+     * ensures that the token stays alive for the lifetime of the app process, allowing us to
+     * revoke permissions when the app process terminates using {@link IBinder#linkToDeath} in
+     * {@code DragAndDropPermissionsHandler}.
+     */
+    private static IBinder sAppToken;
+
+    private final IDragAndDropPermissions mDragAndDropPermissions;
 
     /**
      * Create a new {@link DragAndDropPermissions} object to control the access permissions for
@@ -81,30 +103,51 @@
     }
 
     /**
-     * Take the permissions and bind their lifetime to the activity.
+     * Take permissions, binding their lifetime to the activity.
+     *
+     * <p>Note: This API is exposed to apps via
+     * {@link Activity#requestDragAndDropPermissions(DragEvent)}.
+     *
      * @param activityToken Binder pointing to an Activity instance to bind the lifetime to.
      * @return True if permissions are successfully taken.
+     *
      * @hide
      */
     public boolean take(IBinder activityToken) {
         try {
+            if (DEBUG) {
+                Log.d(TAG, this + ": calling take() with activity-bound token: "
+                        + toHexString(activityToken.hashCode()));
+            }
             mDragAndDropPermissions.take(activityToken);
         } catch (RemoteException e) {
+            Log.w(TAG, this + ": take() failed with a RemoteException", e);
             return false;
         }
         return true;
     }
 
     /**
-     * Take the permissions. Must call {@link #release} explicitly.
+     * Take permissions transiently. Permissions will be revoked when the app process terminates.
+     *
+     * <p>Note: This API is not exposed to apps.
+     *
      * @return True if permissions are successfully taken.
+     *
      * @hide
      */
     public boolean takeTransient() {
         try {
-            mTransientToken = new Binder();
-            mDragAndDropPermissions.takeTransient(mTransientToken);
+            if (sAppToken == null) {
+                sAppToken = new Binder();
+            }
+            if (DEBUG) {
+                Log.d(TAG, this + ": calling takeTransient() with process-bound token: "
+                        + toHexString(sAppToken.hashCode()));
+            }
+            mDragAndDropPermissions.takeTransient(sAppToken);
         } catch (RemoteException e) {
+            Log.w(TAG, this + ": takeTransient() failed with a RemoteException", e);
             return false;
         }
         return true;
@@ -116,8 +159,8 @@
     public void release() {
         try {
             mDragAndDropPermissions.release();
-            mTransientToken = null;
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -142,11 +185,9 @@
     @Override
     public void writeToParcel(Parcel destination, int flags) {
         destination.writeStrongInterface(mDragAndDropPermissions);
-        destination.writeStrongBinder(mTransientToken);
     }
 
     private DragAndDropPermissions(Parcel in) {
         mDragAndDropPermissions = IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());
-        mTransientToken = in.readStrongBinder();
     }
 }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index afbd249..10721ad 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -43,8 +43,11 @@
      * accordingly. This should be called before `finish`
      * @param taskId for which the leash should be updated
      * @param destinationBounds bounds of the final PiP window
+     * @param windowCrop bounds to crop as part of final transform.
+     * @param float9 An array of 9 floats to be used as matrix transform.
      */
-     void setFinishTaskBounds(int taskId, in Rect destinationBounds);
+     void setFinishTaskBounds(int taskId, in Rect destinationBounds, in Rect windowCrop,
+             in float[] float9);
 
     /**
      * Notifies to the system that the animation into Recents should end, and all leashes associated
@@ -142,7 +145,13 @@
      *
      * The system reparents the leash of navigation bar to the app when the recents animation starts
      * and Launcher should call this method to let system restore the navigation bar to its
-     * original position when the quick switch gesture is finished.
+     * original position when the quick switch gesture is finished and will run the fade-in
+     * animation If {@param moveHomeToTop} is {@code true}. Otherwise, restore the navigtation bar
+     * without animation.
+     *
+     * @param moveHomeToTop if {@code true}, the home activity should be moved to the top.
+     *                      Otherwise, the home activity is hidden and the user is returned to the
+     *                      app.
      */
-    void detachNavigationBarFromApp();
+    void detachNavigationBarFromApp(boolean moveHomeToTop);
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5477800..b345b2e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -816,4 +816,6 @@
      * @param listener the listener to be unregistered
      */
     void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
+
+    void setForceCrossWindowBlurDisabled(boolean disable);
 }
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 02a9788..aa1acc1 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.input.InputManager;
 import android.os.Build;
@@ -25,6 +26,8 @@
 import android.util.AndroidRuntimeException;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.text.Normalizer;
 
 /**
@@ -297,6 +300,8 @@
     private static native char nativeGetDisplayLabel(long ptr, int keyCode);
     private static native int nativeGetKeyboardType(long ptr);
     private static native KeyEvent[] nativeGetEvents(long ptr, char[] chars);
+    private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
+    private static native boolean nativeEquals(long ptr1, long ptr2);
 
     private KeyCharacterMap(Parcel in) {
         if (in == null) {
@@ -323,6 +328,18 @@
     }
 
     /**
+     * Obtain empty key character map
+     * @param deviceId The input device ID
+     * @return The KeyCharacterMap object
+     * @hide
+     */
+    @VisibleForTesting
+    @Nullable
+    public static KeyCharacterMap obtainEmptyMap(int deviceId) {
+        return nativeObtainEmptyKeyCharacterMap(deviceId);
+    }
+
+    /**
      * Loads the key character maps for the keyboard with the specified device id.
      *
      * @param deviceId The device id of the keyboard.
@@ -729,6 +746,18 @@
         return 0;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof KeyCharacterMap)) {
+            return false;
+        }
+        KeyCharacterMap peer = (KeyCharacterMap) obj;
+        if (mPtr == 0 || peer.mPtr == 0) {
+            return mPtr == peer.mPtr;
+        }
+        return nativeEquals(mPtr, peer.mPtr);
+    }
+
     /**
      * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded.
      */
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index cbb86de..31f6f6a 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -78,3 +78,8 @@
 per-file ViewRootInsetsControllerHost.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.aidl = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index cc7525b..56b4383 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -163,7 +163,7 @@
      * @hide
      */
     public boolean isEmpty() {
-        return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
+        return mRadius == 0 || mCenter.x <= 0 || mCenter.y <= 0;
     }
 
     private String getPositionString(@Position int position) {
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index 569c287..623d969 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -181,16 +181,16 @@
         boolean hasRoundedCorner;
         switch (position) {
             case POSITION_TOP_LEFT:
-                hasRoundedCorner = radius > insetTop || radius > insetLeft;
+                hasRoundedCorner = radius > insetTop && radius > insetLeft;
                 break;
             case POSITION_TOP_RIGHT:
-                hasRoundedCorner = radius > insetTop || radius > insetRight;
+                hasRoundedCorner = radius > insetTop && radius > insetRight;
                 break;
             case POSITION_BOTTOM_RIGHT:
-                hasRoundedCorner = radius > insetBottom || radius > insetRight;
+                hasRoundedCorner = radius > insetBottom && radius > insetRight;
                 break;
             case POSITION_BOTTOM_LEFT:
-                hasRoundedCorner = radius > insetBottom || radius > insetLeft;
+                hasRoundedCorner = radius > insetBottom && radius > insetLeft;
                 break;
             default:
                 throw new IllegalArgumentException(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9fc415d..35726c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8572,6 +8572,17 @@
         }
 
         @Override
+        public void onDragEvent(boolean isExiting, float x, float y) {
+            // force DRAG_EXITED_EVENT if appropriate
+            DragEvent event = DragEvent.obtain(
+                    isExiting ? DragEvent.ACTION_DRAG_EXITED : DragEvent.ACTION_DRAG_LOCATION,
+                    x, y, 0 /* offsetX */, 0 /* offsetY */, null/* localState */,
+                    null/* description */, null /* data */, null /* dragSurface */,
+                    null /* dragAndDropPermissions */, false /* result */);
+            dispatchDragEvent(event);
+        }
+
+        @Override
         public void dispose() {
             unscheduleConsumeBatchedInput();
             super.dispose();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7338c7d..04512c9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -81,6 +81,7 @@
 import static android.view.WindowLayoutParamsProto.Y;
 
 import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -121,6 +122,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -864,6 +866,33 @@
     }
 
     /**
+     * Adds a listener, which will be called when cross-window blurs are enabled/disabled at
+     * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius})
+     * and window background blur (see {@link Window#setBackgroundBlurRadius}).
+     *
+     * Cross-window blur might not be supported by some devices due to GPU limitations. It can also
+     * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+     * when minimal post processing is requested. In such situations, no blur will be computed or
+     * drawn, so the blur target area will not be blurred. To handle this, the app might want to
+     * change its theme to one that does not use blurs.
+     *
+     * If the listener is added successfully, it will be called immediately with the current
+     * cross-window blur enabled state.
+     *
+     * @param executor {@link Executor} to handle the listener callback
+     * @param listener the listener to be added. It will be called back with a boolean parameter,
+     *                 which is true if cross-window blur is enabled and false if it is disabled
+     *
+     * @see #removeCrossWindowBlurEnabledListener
+     * @see #isCrossWindowBlurEnabled
+     * @see LayoutParams#setBlurBehindRadius
+     * @see Window#setBackgroundBlurRadius
+     */
+    default void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> listener) {
+    }
+
+    /**
      * Removes a listener, previously added with {@link #addCrossWindowBlurEnabledListener}
      *
      * @param listener the listener to be removed
@@ -873,6 +902,20 @@
     default void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
     }
 
+    /**
+     * Disables cross-window blurs device-wide. This includes window blur behind
+     * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur
+     * (see {@link Window#setBackgroundBlurRadius}).
+     *
+     * @param disable specifies whether to disable the blur. Note that calling this
+     *                with 'disable=false' will not enable blurs if there is something
+     *                else disabling blurs.
+     * @hide
+     */
+    @TestApi
+    default void setForceCrossWindowBlurDisabled(boolean disable) {
+    }
+
     public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index b398707..8dce852 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
@@ -40,6 +41,7 @@
 import com.android.internal.os.IResultReceiver;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -310,11 +312,26 @@
 
     @Override
     public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
-        CrossWindowBlurListeners.getInstance().addListener(listener);
+        addCrossWindowBlurEnabledListener(mContext.getMainExecutor(), listener);
+    }
+
+    @Override
+    public void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> listener) {
+        CrossWindowBlurListeners.getInstance().addListener(executor, listener);
     }
 
     @Override
     public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
         CrossWindowBlurListeners.getInstance().removeListener(listener);
     }
+
+    @Override
+    public void setForceCrossWindowBlurDisabled(boolean disable) {
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                .setForceCrossWindowBlurDisabled(disable);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6ade5e6..de4554b 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -105,7 +105,7 @@
      */
     @MainThread
     default void initializeInternal(IBinder token, int displayId,
-            IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+            IInputMethodPrivilegedOperations privilegedOperations) {
         updateInputMethodDisplay(displayId);
         attachToken(token);
     }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 25712f8..cc533eb 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -18,23 +18,19 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
-import android.inputmethodservice.InputMethodService;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -64,7 +60,7 @@
  * @attr ref android.R.styleable#InputMethod_isDefault
  * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
  * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
- * @attr ref android.R.styleable#InputMethod_configChanges
+ * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
  */
 public final class InputMethodInfo implements Parcelable {
     static final String TAG = "InputMethodInfo";
@@ -123,10 +119,9 @@
     private final boolean mInlineSuggestionsEnabled;
 
     /**
-     * The flag for configurations IME assumes the responsibility for handling in
-     * {@link InputMethodService#onConfigurationChanged(Configuration)}}.
+     * The flag whether this IME suppresses spell checker.
      */
-    private final int mHandledConfigChanges;
+    private final boolean mSuppressesSpellChecker;
 
     /**
      * @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
@@ -171,6 +166,7 @@
         boolean isAuxIme = true;
         boolean supportsSwitchingToNextInputMethod = false; // false as default
         boolean inlineSuggestionsEnabled = false; // false as default
+        boolean suppressesSpellChecker = false; // false as default
         mForceDefault = false;
 
         PackageManager pm = context.getPackageManager();
@@ -214,8 +210,8 @@
                     false);
             inlineSuggestionsEnabled = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
-            mHandledConfigChanges = sa.getInt(
-                    com.android.internal.R.styleable.InputMethod_configChanges, 0);
+            suppressesSpellChecker = sa.getBoolean(
+                    com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
             sa.recycle();
 
             final int depth = parser.getDepth();
@@ -287,6 +283,7 @@
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
         mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+        mSuppressesSpellChecker = suppressesSpellChecker;
         mIsVrOnly = isVrOnly;
     }
 
@@ -297,10 +294,10 @@
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
         mInlineSuggestionsEnabled = source.readInt() == 1;
+        mSuppressesSpellChecker = source.readBoolean();
         mIsVrOnly = source.readBoolean();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
         mSubtypes = new InputMethodSubtypeArray(source);
-        mHandledConfigChanges = source.readInt();
         mForceDefault = false;
     }
 
@@ -312,22 +309,7 @@
         this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
                 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
-                false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
-                0 /* handledConfigChanges */);
-    }
-
-    /**
-     * Temporary API for creating a built-in input method for test.
-     * @hide
-     */
-    @TestApi
-    public InputMethodInfo(@NonNull String packageName, @NonNull String className,
-            @NonNull CharSequence label, @NonNull String settingsActivity,
-            int handledConfigChanges) {
-        this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
-                settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
-                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
-                false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
+                false /* inlineSuggestionsEnabled */, false /* isVrOnly */);
     }
 
     /**
@@ -339,7 +321,7 @@
             boolean forceDefault) {
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
                 true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
-                false /* isVrOnly */, 0 /* handledconfigChanges */);
+                false /* isVrOnly */);
     }
 
     /**
@@ -350,8 +332,7 @@
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
             boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
-                supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
-                0 /* handledConfigChanges */);
+                supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly);
     }
 
     /**
@@ -361,7 +342,7 @@
     public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
             boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
-            boolean isVrOnly, int handledConfigChanges) {
+            boolean isVrOnly) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -372,8 +353,8 @@
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
         mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+        mSuppressesSpellChecker = false;
         mIsVrOnly = isVrOnly;
-        mHandledConfigChanges = handledConfigChanges;
     }
 
     private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -520,23 +501,13 @@
         }
     }
 
-    /**
-     * Returns the bit mask of kinds of configuration changes that this IME
-     * can handle itself (without being restarted by the system).
-     *
-     * @attr ref android.R.styleable#InputMethod_configChanges
-     */
-    @ActivityInfo.Config
-    public int getConfigChanges() {
-        return mHandledConfigChanges;
-    }
-
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
                 + " mIsVrOnly=" + mIsVrOnly
                 + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
-                + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled);
+                + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+                + " mSuppressesSpellChecker=" + mSuppressesSpellChecker);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
         pw.println(prefix + "Service:");
@@ -605,6 +576,13 @@
     }
 
     /**
+     * Return {@code true} if this input method suppresses spell checker.
+     */
+    public boolean suppressesSpellChecker() {
+        return mSuppressesSpellChecker;
+    }
+
+    /**
      * Used to package this object into a {@link Parcel}.
      *
      * @param dest The {@link Parcel} to be written.
@@ -618,10 +596,10 @@
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
         dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0);
+        dest.writeBoolean(mSuppressesSpellChecker);
         dest.writeBoolean(mIsVrOnly);
         mService.writeToParcel(dest, flags);
         mSubtypes.writeToParcel(dest);
-        dest.writeInt(mHandledConfigChanges);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a8fff8b..ff4d671 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -106,7 +106,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.Collections;
@@ -413,7 +412,7 @@
      * The InputConnection that was last retrieved from the served view.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    ControlledInputConnectionWrapper mServedInputConnectionWrapper;
+    IInputConnectionWrapper mServedInputConnectionWrapper;
     /**
      * The completions that were last provided by the served view.
      */
@@ -445,6 +444,13 @@
      */
     private Matrix mActivityViewToScreenMatrix = null;
 
+    /**
+     * As reported by {@link InputBindResult}. This value is determined by
+     * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecking}.
+     */
+    @GuardedBy("mH")
+    private boolean mIsInputMethodSuppressingSpellChecker = false;
+
     // -----------------------------------------------------------
 
     /**
@@ -740,8 +746,7 @@
         /**
          * Checks whether the active input connection (if any) is for the given view.
          *
-         * TODO(b/160968797): Remove this method and move mServedInputConnectionWrapper to
-         *  ImeFocusController.
+         * TODO(b/182259171): Clean-up hasActiveConnection to simplify the logic.
          *
          * Note that this method is only intended for restarting input after focus gain
          * (e.g. b/160391516), DO NOT leverage this method to do another check.
@@ -755,7 +760,7 @@
 
                 return mServedInputConnectionWrapper != null
                         && mServedInputConnectionWrapper.isActive()
-                        && mServedInputConnectionWrapper.mServedView.get() == view;
+                        && mServedInputConnectionWrapper.getServedView() == view;
             }
         }
     }
@@ -860,6 +865,8 @@
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                         mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+                        mIsInputMethodSuppressingSpellChecker =
+                                res.isInputMethodSuppressingSpellChecker;
                     }
                     startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
                     return;
@@ -1022,77 +1029,6 @@
         }
     }
 
-    private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
-        private final InputMethodManager mParentInputMethodManager;
-        private final WeakReference<View> mServedView;
-
-        ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn,
-                InputMethodManager inputMethodManager, View servedView) {
-            super(icLooper, conn);
-            mParentInputMethodManager = inputMethodManager;
-            mServedView = new WeakReference<>(servedView);
-        }
-
-        @Override
-        public boolean isActive() {
-            return mParentInputMethodManager.mActive && !isFinished();
-        }
-
-        @Override
-        public InputMethodManager getIMM() {
-            return mParentInputMethodManager;
-        }
-
-        void deactivate() {
-            if (isFinished()) {
-                // This is a small performance optimization.  Still only the 1st call of
-                // reportFinish() will take effect.
-                return;
-            }
-            closeConnection();
-
-            // Notify the app that the InputConnection was closed.
-            final View servedView = mServedView.get();
-            if (servedView != null) {
-                final Handler handler = servedView.getHandler();
-                // The handler is null if the view is already detached. When that's the case, for
-                // now, we simply don't dispatch this callback.
-                if (handler != null) {
-                    if (DEBUG) {
-                        Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
-                    }
-                    if (handler.getLooper().isCurrentThread()) {
-                        servedView.onInputConnectionClosedInternal();
-                    } else {
-                        handler.post(servedView::onInputConnectionClosedInternal);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "ControlledInputConnectionWrapper{"
-                    + "connection=" + getInputConnection()
-                    + " finished=" + isFinished()
-                    + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
-                    + " mServedView=" + mServedView.get()
-                    + "}";
-        }
-
-        void dumpDebug(ProtoOutputStream proto, long fieldId) {
-            // Check that the call is initiated in the main thread of the current InputConnection
-            // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
-            // executed on this thread. Otherwise the messages are dispatched to the correct thread
-            // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
-            // reasons.
-            if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper()
-                    == getLooper()) {
-                ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId);
-            }
-        }
-    }
-
     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
         @Override
         protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -1256,8 +1192,7 @@
         mMainLooper = looper;
         mH = new H(looper);
         mDisplayId = displayId;
-        mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this,
-                null);
+        mIInputContext = new IInputConnectionWrapper(looper, mDummyInputConnection, this, null);
     }
 
     /**
@@ -1373,7 +1308,9 @@
      * @return {@link List} of {@link InputMethodInfo}.
      * @hide
      */
+    @TestApi
     @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+    @NonNull
     public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
         try {
             final Completable.InputMethodInfoList value = Completable.createInputMethodInfoList();
@@ -1544,6 +1481,15 @@
     }
 
     /**
+     * Return {@code true} if the input method is suppressing system spell checker.
+     */
+    public boolean isInputMethodSuppressingSpellChecker() {
+        synchronized (mH) {
+            return mIsInputMethodSuppressingSpellChecker;
+        }
+    }
+
+    /**
      * Reset all of the state associated with being bound to an input method.
      */
     void clearBindingLocked() {
@@ -1587,6 +1533,7 @@
     @UnsupportedAppUsage
     void finishInputLocked() {
         mActivityViewToScreenMatrix = null;
+        mIsInputMethodSuppressingSpellChecker = false;
         setNextServedViewLocked(null);
         if (getServedViewLocked() != null) {
             if (DEBUG) {
@@ -2063,7 +2010,7 @@
                 mServedInputConnectionWrapper.deactivate();
                 mServedInputConnectionWrapper = null;
             }
-            ControlledInputConnectionWrapper servedContext;
+            IInputConnectionWrapper servedContext;
             final int missingMethodFlags;
             if (ic != null) {
                 mCursorSelStart = tba.initialSelStart;
@@ -2080,7 +2027,7 @@
                 } else {
                     icHandler = ic.getHandler();
                 }
-                servedContext = new ControlledInputConnectionWrapper(
+                servedContext = new IInputConnectionWrapper(
                         icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
             } else {
                 servedContext = null;
@@ -2111,6 +2058,7 @@
                     return false;
                 }
                 mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
+                mIsInputMethodSuppressingSpellChecker = res.isInputMethodSuppressingSpellChecker;
                 if (res.id != null) {
                     setInputChannelLocked(res.channel);
                     mBindSequence = res.sequence;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 6d5077a..ef50045 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -145,7 +145,7 @@
     @StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_EDITTEXT, WIDGET_TYPE_UNSELECTABLE_TEXTVIEW,
             WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW,
             WIDGET_TYPE_CUSTOM_EDITTEXT, WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW,
-            WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_UNKNOWN})
+            WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_CLIPBOARD, WIDGET_TYPE_UNKNOWN })
     @interface WidgetType {}
 
     /** The widget involved in the text classification context is a standard
@@ -172,6 +172,8 @@
     String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
     /** The widget involved in the text classification context is a notification */
     String WIDGET_TYPE_NOTIFICATION = "notification";
+    /** The text classification context is for use with the system clipboard. */
+    String WIDGET_TYPE_CLIPBOARD = "clipboard";
     /** The widget involved in the text classification context is of an unknown/unspecified type. */
     String WIDGET_TYPE_UNKNOWN = "unknown";
 
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 35d8445..ba58b65 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -25,6 +25,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.textservice.ISpellCheckerSession;
 import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -176,6 +177,11 @@
      * @param suggestionsLimit the maximum number of suggestions that will be returned
      */
     public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) {
+        final InputMethodManager imm = mTextServicesManager.getInputMethodManager();
+        if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+            handleOnGetSentenceSuggestionsMultiple(new SentenceSuggestionsInfo[0]);
+            return;
+        }
         mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
                 textInfos, suggestionsLimit);
     }
@@ -204,6 +210,11 @@
         if (DBG) {
             Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
         }
+        final InputMethodManager imm = mTextServicesManager.getInputMethodManager();
+        if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+            handleOnGetSuggestionsMultiple(new SuggestionsInfo[0]);
+            return;
+        }
         mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
                 textInfos, suggestionsLimit, sequentialWords);
     }
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 996757d..6fb01a3 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -30,6 +30,7 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.UserHandle;
 import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 
 import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -88,10 +89,15 @@
     @UserIdInt
     private final int mUserId;
 
-    private TextServicesManager(@UserIdInt int userId) throws ServiceNotFoundException {
+    @Nullable
+    private final InputMethodManager mInputMethodManager;
+
+    private TextServicesManager(@UserIdInt int userId,
+            @Nullable InputMethodManager inputMethodManager) throws ServiceNotFoundException {
         mService = ITextServicesManager.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
         mUserId = userId;
+        mInputMethodManager = inputMethodManager;
     }
 
     /**
@@ -105,7 +111,8 @@
     @NonNull
     public static TextServicesManager createInstance(@NonNull Context context)
             throws ServiceNotFoundException {
-        return new TextServicesManager(context.getUserId());
+        return new TextServicesManager(context.getUserId(), context.getSystemService(
+                InputMethodManager.class));
     }
 
     /**
@@ -118,7 +125,7 @@
         synchronized (TextServicesManager.class) {
             if (sInstance == null) {
                 try {
-                    sInstance = new TextServicesManager(UserHandle.myUserId());
+                    sInstance = new TextServicesManager(UserHandle.myUserId(), null);
                 } catch (ServiceNotFoundException e) {
                     throw new IllegalStateException(e);
                 }
@@ -127,6 +134,12 @@
         }
     }
 
+    /** @hide */
+    @Nullable
+    public InputMethodManager getInputMethodManager() {
+        return mInputMethodManager;
+    }
+
     /**
      * Returns the language component of a given locale string.
      */
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index cf3358b..9f90b3b 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -25,6 +25,7 @@
 import android.annotation.WorkerThread;
 import android.app.Activity;
 import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
@@ -54,6 +55,11 @@
  */
 public class UiTranslationController {
 
+    // TODO(b/182433547): remove Build.IS_DEBUGGABLE before ship. Enable the logging in debug build
+    //  to help the debug during the development phase
+    public static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)
+            || Build.IS_DEBUGGABLE;
+
     private static final String TAG = "UiTranslationController";
     @NonNull
     private final Activity mActivity;
@@ -93,6 +99,8 @@
         if (!mActivity.isResumed()) {
             return;
         }
+        Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
+                + (DEBUG ? ", views: " + views : ""));
         switch (state) {
             case STATE_UI_TRANSLATION_STARTED:
                 final Pair<TranslationSpec, TranslationSpec> specs =
@@ -149,8 +157,69 @@
             translator.dump(outerPrefix, pw);
             pw.println();
         }
+        synchronized (mLock) {
+            final int viewSize = mViews.size();
+            pw.print(outerPrefix); pw.print("number views: "); pw.println(viewSize);
+            for (int i = 0; i < viewSize; i++) {
+                pw.print(outerPrefix); pw.print("#"); pw.println(i);
+                final AutofillId autofillId = mViews.keyAt(i);
+                final View view = mViews.valueAt(i).get();
+                pw.print(pfx); pw.print("autofillId: "); pw.println(autofillId);
+                pw.print(pfx); pw.print("view:"); pw.println(view);
+            }
+        }
+        // TODO(b/182433547): we will remove debug rom condition before S release then we change
+        //  change this back to "DEBUG"
+        if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
+            dumpViewByTraversal(outerPrefix, pw);
+        }
     }
 
+    private void dumpViewByTraversal(String outerPrefix, PrintWriter pw) {
+        final ArrayList<ViewRootImpl> roots =
+                WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+        pw.print(outerPrefix); pw.println("Dump views:");
+        for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+            final View rootView = roots.get(rootNum).getView();
+            if (rootView instanceof ViewGroup) {
+                dumpChildren((ViewGroup) rootView, outerPrefix, pw);
+            } else {
+                dumpViewInfo(rootView, outerPrefix, pw);
+            }
+        }
+    }
+
+    private void dumpChildren(ViewGroup viewGroup, String outerPrefix, PrintWriter pw) {
+        final int childCount = viewGroup.getChildCount();
+        for (int i = 0; i < childCount; ++i) {
+            final View child = viewGroup.getChildAt(i);
+            if (child instanceof ViewGroup) {
+                pw.print(outerPrefix); pw.println("Children: ");
+                pw.print(outerPrefix); pw.print(outerPrefix); pw.println(child);
+                dumpChildren((ViewGroup) child, outerPrefix, pw);
+            } else {
+                pw.print(outerPrefix); pw.println("End Children: ");
+                pw.print(outerPrefix); pw.print(outerPrefix); pw.print(child);
+                dumpViewInfo(child, outerPrefix, pw);
+            }
+        }
+    }
+
+    private void dumpViewInfo(View view, String outerPrefix, PrintWriter pw) {
+        final AutofillId autofillId = view.getAutofillId();
+        pw.print(outerPrefix); pw.print("autofillId: "); pw.print(autofillId);
+        // TODO: print TranslationTransformation
+        boolean isContainsView = false;
+        synchronized (mLock) {
+            final WeakReference<View> viewRef = mViews.get(autofillId);
+            if (viewRef != null && viewRef.get() != null) {
+                isContainsView = true;
+            }
+        }
+        pw.print(outerPrefix); pw.print("isContainsView: "); pw.println(isContainsView);
+    }
+
+
     /**
      * The method is used by {@link Translator}, it will be called when the translation is done. The
      * translation result can be get from here.
@@ -171,6 +240,9 @@
             return;
         }
         final int resultCount = translatedResult.size();
+        if (DEBUG) {
+            Log.v(TAG, "onTranslationCompleted: receive " + resultCount + " responses.");
+        }
         synchronized (mLock) {
             for (int i = 0; i < resultCount; i++) {
                 final ViewTranslationResponse response = translatedResult.get(i);
@@ -180,7 +252,7 @@
                 }
                 final View view = mViews.get(autofillId).get();
                 if (view == null) {
-                    Log.w(TAG, "onTranslationCompleted: the Veiew for autofill id " + autofillId
+                    Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId
                             + " may be gone.");
                     continue;
                 }
@@ -208,6 +280,10 @@
     @WorkerThread
     private void sendTranslationRequest(Translator translator,
             List<ViewTranslationRequest> requests) {
+        if (requests.size() == 0) {
+            Log.wtf(TAG, "No ViewTranslationRequest was collected.");
+            return;
+        }
         final TranslationRequest request = new TranslationRequest.Builder()
                 .setViewTranslationRequests(requests)
                 .build();
@@ -233,7 +309,8 @@
                         requests.add(request);
                     }
                     if (currentCount == (foundViews.size() - 1)) {
-                        Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request.");
+                        Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
+                                + " requests.");
                         mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
                                 UiTranslationController::sendTranslationRequest,
                                 UiTranslationController.this, translator, requests));
@@ -287,6 +364,9 @@
                 for (int i = 0; i < viewCounts; i++) {
                     final View view = views.valueAt(i).get();
                     if (view == null) {
+                        if (DEBUG) {
+                            Log.d(TAG, "View was gone for autofillid = " + views.keyAt(i));
+                        }
                         continue;
                     }
                     action.accept(view);
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index a3a6a2e..7c73e70 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -43,6 +43,14 @@
     private static final String TAG = "UiTranslationManager";
 
     /**
+     * The tag which uses for enabling debug log dump. To enable it, we can use command "adb shell
+     * setprop log.tag.UiTranslation DEBUG".
+     *
+     * @hide
+     */
+    public static final String LOG_TAG = "UiTranslation";
+
+    /**
      * The state caller request to disable utranslation,, it is no longer need to ui translation.
      *
      * @hide
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 34fe51e..42d7535 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -200,6 +200,8 @@
         mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
         createClock();
 
+        a.recycle();
+
         mDialWidth = mDial.getIntrinsicWidth();
         mDialHeight = mDial.getIntrinsicHeight();
     }
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 45352e4..c203c790 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -130,7 +130,7 @@
     public @interface EdgeEffectType {
     }
 
-    private static final float DEFAULT_MAX_STRETCH_INTENSITY = 1.5f;
+    private static final float DEFAULT_MAX_STRETCH_INTENSITY = 0.08f;
 
     @SuppressWarnings("UnusedDeclaration")
     private static final String TAG = "EdgeEffect";
@@ -177,6 +177,7 @@
     private long mStartTime;
     private float mDuration;
     private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
+    private float mStretchDistanceFraction = 0.1f;
     private float mStretchDistance = -1f;
 
     private final Interpolator mInterpolator = new DecelerateInterpolator();
@@ -467,7 +468,7 @@
     public void onAbsorb(int velocity) {
         if (mEdgeEffectType == TYPE_STRETCH) {
             mState = STATE_RECEDE;
-            mVelocity = velocity / mHeight;
+            mVelocity = velocity;
             mDistance = 0;
             mStartTime = AnimationUtils.currentAnimationTimeMillis();
         } else {
@@ -655,7 +656,7 @@
                     //  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))
+                            (mStretchDistanceFraction * Math.min(mWidth, mHeight))
             );
         }
 
@@ -745,9 +746,9 @@
         final double mDampedFreq = NATURAL_FREQUENCY * Math.sqrt(1 - DAMPING_RATIO * DAMPING_RATIO);
 
         // We're always underdamped, so we can use only those equations:
-        double cosCoeff = mDistance;
+        double cosCoeff = mDistance * mHeight;
         double sinCoeff = (1 / mDampedFreq) * (DAMPING_RATIO * NATURAL_FREQUENCY
-                * mDistance + mVelocity);
+                * mDistance * mHeight + mVelocity);
         double distance = Math.pow(Math.E, -DAMPING_RATIO * NATURAL_FREQUENCY * deltaT)
                 * (cosCoeff * Math.cos(mDampedFreq * deltaT)
                 + sinCoeff * Math.sin(mDampedFreq * deltaT));
@@ -755,7 +756,7 @@
                 + Math.pow(Math.E, -DAMPING_RATIO * NATURAL_FREQUENCY * deltaT)
                 * (-mDampedFreq * cosCoeff * Math.sin(mDampedFreq * deltaT)
                 + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
-        mDistance = (float) distance;
+        mDistance = (float) distance / mHeight;
         mVelocity = (float) velocity;
         mStartTime = time;
         if (isAtEquilibrium()) {
@@ -786,9 +787,8 @@
      * considered at rest or false if it is still animating.
      */
     private boolean isAtEquilibrium() {
-        double velocity = mVelocity * mHeight; // in pixels/second
         double displacement = mDistance * mHeight; // in pixels
-        return Math.abs(velocity) < VELOCITY_THRESHOLD
+        return Math.abs(mVelocity) < VELOCITY_THRESHOLD
                 && Math.abs(displacement) < VALUE_THRESHOLD;
     }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 012352d..238ce85 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -988,6 +988,12 @@
 
         if (mTextView.isTextEditable() && mTextView.isSuggestionsEnabled()
                 && !(mTextView.isInExtractedMode())) {
+            final InputMethodManager imm = getInputMethodManager();
+            if (imm != null && imm.isInputMethodSuppressingSpellChecker()) {
+                // Do not close mSpellChecker here as it may be reused when the current IME has been
+                // changed.
+                return;
+            }
             if (mSpellChecker == null && createSpellChecker) {
                 mSpellChecker = new SpellChecker(mTextView);
             }
@@ -2898,9 +2904,6 @@
         } finally {
             mTextView.endBatchEdit();
             mUndoInputFilter.freezeLastEdit();
-            if (permissions != null) {
-                permissions.release();
-            }
         }
     }
 
@@ -3790,7 +3793,7 @@
         }
 
         public SuggestionsPopupWindow() {
-            mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+            mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr();
         }
 
         @Override
@@ -3957,7 +3960,7 @@
             }
 
             if (updateSuggestions()) {
-                mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+                mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr();
                 mTextView.setCursorVisible(false);
                 mIsShowingUp = true;
                 super.show();
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
new file mode 100644
index 0000000..d843308
--- /dev/null
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -0,0 +1,217 @@
+/*
+ * 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.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseIntArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.ColorResources;
+import android.widget.RemoteViews.InteractionHandler;
+import android.widget.RemoteViews.RemoteCollectionItems;
+
+import com.android.internal.R;
+
+import java.util.stream.IntStream;
+
+/**
+ * List {@link Adapter} backed by a {@link RemoteCollectionItems}.
+ *
+ * @hide
+ */
+class RemoteCollectionItemsAdapter extends BaseAdapter {
+
+    private final int mViewTypeCount;
+
+    private RemoteCollectionItems mItems;
+    private InteractionHandler mInteractionHandler;
+    private ColorResources mColorResources;
+
+    private SparseIntArray mLayoutIdToViewType;
+
+    RemoteCollectionItemsAdapter(
+            @NonNull RemoteCollectionItems items,
+            @NonNull InteractionHandler interactionHandler,
+            @NonNull ColorResources colorResources) {
+        // View type count can never increase after an adapter has been set on a ListView.
+        // Additionally, decreasing it could inhibit view recycling if the count were to back and
+        // forth between 3-2-3-2 for example. Therefore, the view type count, should be fixed for
+        // the lifetime of the adapter.
+        mViewTypeCount = items.getViewTypeCount();
+
+        mItems = items;
+        mInteractionHandler = interactionHandler;
+        mColorResources = colorResources;
+
+        initLayoutIdToViewType();
+    }
+
+    /**
+     * Updates the data for the adapter, allowing recycling of views. Note that if the view type
+     * count has increased, a new adapter should be created and set on the AdapterView instead of
+     * calling this method.
+     */
+    void setData(
+            @NonNull RemoteCollectionItems items,
+            @NonNull InteractionHandler interactionHandler,
+            @NonNull ColorResources colorResources) {
+        if (mViewTypeCount < items.getViewTypeCount()) {
+            throw new IllegalArgumentException(
+                    "RemoteCollectionItemsAdapter cannot increase view type count after creation");
+        }
+
+        mItems = items;
+        mInteractionHandler = interactionHandler;
+        mColorResources = colorResources;
+
+        initLayoutIdToViewType();
+
+        notifyDataSetChanged();
+    }
+
+    private void initLayoutIdToViewType() {
+        SparseIntArray previousLayoutIdToViewType = mLayoutIdToViewType;
+        mLayoutIdToViewType = new SparseIntArray(mViewTypeCount);
+
+        int[] layoutIds = IntStream.range(0, mItems.getItemCount())
+                .map(position -> mItems.getItemView(position).getLayoutId())
+                .distinct()
+                .toArray();
+        if (layoutIds.length > mViewTypeCount) {
+            throw new IllegalArgumentException(
+                    "Collection items uses " + layoutIds.length + " distinct layouts, which is "
+                            + "more than view type count of " + mViewTypeCount);
+        }
+
+        // Tracks whether a layout id (by index, not value) has been assigned a view type.
+        boolean[] processedLayoutIdIndices = new boolean[layoutIds.length];
+        // Tracks whether a view type has been assigned to a layout id already.
+        boolean[] assignedViewTypes = new boolean[mViewTypeCount];
+
+        if (previousLayoutIdToViewType != null) {
+            for (int i = 0; i < layoutIds.length; i++) {
+                int layoutId = layoutIds[i];
+                // Copy over any previously used view types for layout ids in the collection to keep
+                // view types stable across data updates.
+                int previousViewType = previousLayoutIdToViewType.get(layoutId, -1);
+                // Skip this layout id if it wasn't assigned to a view type previously.
+                if (previousViewType < 0) continue;
+
+                mLayoutIdToViewType.put(layoutId, previousViewType);
+                processedLayoutIdIndices[i] = true;
+                assignedViewTypes[previousViewType] = true;
+            }
+        }
+
+        int lastViewType = -1;
+        for (int i = 0; i < layoutIds.length; i++) {
+            // If a view type has already been assigned to the layout id, skip it.
+            if (processedLayoutIdIndices[i]) continue;
+
+            int layoutId = layoutIds[i];
+            // If no view type is assigned for the layout id, choose the next possible value that
+            // isn't already assigned to a layout id. There is guaranteed to be some value available
+            // due to the prior validation logic that count(distinct layout ids) <= viewTypeCount.
+            int viewType = IntStream.range(lastViewType + 1, layoutIds.length)
+                    .filter(type -> !assignedViewTypes[type])
+                    .findFirst()
+                    .orElseThrow(
+                            () -> new IllegalStateException(
+                                    "RemoteCollectionItems has more distinct layout ids than its "
+                                            + "view type count"));
+            mLayoutIdToViewType.put(layoutId, viewType);
+            processedLayoutIdIndices[i] = true;
+            assignedViewTypes[viewType] = true;
+            lastViewType = viewType;
+        }
+    }
+
+    @Override
+    public int getCount() {
+        return mItems.getItemCount();
+    }
+
+    @Override
+    public RemoteViews getItem(int position) {
+        return mItems.getItemView(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mItems.getItemId(position);
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return mLayoutIdToViewType.get(mItems.getItemView(position).getLayoutId());
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return mViewTypeCount;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return mItems.hasStableIds();
+    }
+
+    @Nullable
+    @Override
+    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+        if (position >= getCount()) return null;
+
+        RemoteViews item = mItems.getItemView(position);
+        item.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
+        View reapplyView = getViewToReapply(convertView, item);
+
+        // Reapply the RemoteViews if we can.
+        if (reapplyView != null) {
+            try {
+                item.reapply(
+                        parent.getContext(),
+                        reapplyView,
+                        mInteractionHandler,
+                        null /* size */,
+                        mColorResources);
+                return reapplyView;
+            } catch (RuntimeException e) {
+                // We can't reapply for some reason, we'll fallback to an apply and inflate a
+                // new view.
+            }
+        }
+
+        return item.apply(
+                parent.getContext(),
+                parent,
+                mInteractionHandler,
+                null /* size */,
+                mColorResources);
+    }
+
+    /** Returns {@code convertView} if it can be used to reapply {@code item}, or null otherwise. */
+    @Nullable
+    private static View getViewToReapply(@Nullable View convertView, @NonNull RemoteViews item) {
+        if (convertView == null) return null;
+
+        Object layoutIdTag = convertView.getTag(R.id.widget_frame);
+        if (!(layoutIdTag instanceof Integer)) return null;
+
+        return item.getLayoutId() == (Integer) layoutIdTag ? convertView : null;
+    }
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2328e58..21589c9 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -28,6 +28,7 @@
 import android.annotation.Px;
 import android.annotation.StringRes;
 import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -74,6 +75,7 @@
 import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.Log;
+import android.util.LongArray;
 import android.util.Pair;
 import android.util.SizeF;
 import android.util.SparseIntArray;
@@ -112,6 +114,7 @@
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -120,6 +123,7 @@
 import java.util.Stack;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * A class that describes a view hierarchy that can be displayed in
@@ -223,6 +227,7 @@
     private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
     private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
     private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30;
+    private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31;
 
     /** @hide **/
     @IntDef(prefix = "MARGIN_", value = {
@@ -898,6 +903,72 @@
         ArrayList<RemoteViews> list;
     }
 
+    private static class SetRemoteCollectionItemListAdapterAction extends Action {
+        private final RemoteCollectionItems mItems;
+
+        SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) {
+            viewId = id;
+            mItems = items;
+        }
+
+        SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            mItems = parcel.readTypedObject(RemoteCollectionItems.CREATOR);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+            dest.writeTypedObject(mItems, flags);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) throws ActionException {
+            View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            if (!(target instanceof AdapterView)) {
+                Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not "
+                        + "an AdapterView (id: " + viewId + ")");
+                return;
+            }
+
+            AdapterView adapterView = (AdapterView) target;
+            Adapter adapter = adapterView.getAdapter();
+            // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
+            // count hasn't increased. Note that AbsListView allocates a fixed size array for view
+            // recycling in setAdapter, so we must call setAdapter again if the number of view types
+            // increases.
+            if (adapter instanceof RemoteCollectionItemsAdapter
+                    && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
+                try {
+                    ((RemoteCollectionItemsAdapter) adapter).setData(
+                            mItems, handler, colorResources);
+                } catch (Throwable throwable) {
+                    // setData should never failed with the validation in the items builder, but if
+                    // it does, catch and rethrow.
+                    throw new ActionException(throwable);
+                }
+                return;
+            }
+
+            try {
+                adapterView.setAdapter(
+                        new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+            } catch (Throwable throwable) {
+                // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
+                // a type error.
+                throw new ActionException(throwable);
+            }
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG;
+        }
+    }
+
     private class SetRemoteViewsAdapterIntent extends Action {
         public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
             this.viewId = id;
@@ -1993,22 +2064,79 @@
         mIsRoot = false;
     }
 
+    private static boolean hasStableId(View view) {
+        Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
+        return tag != null;
+    }
+
+    private static int getStableId(View view) {
+        Integer id = (Integer) view.getTag(com.android.internal.R.id.remote_views_stable_id);
+        return id == null ? ViewGroupActionAdd.NO_ID : id;
+    }
+
+    private static void setStableId(View view, int stableId) {
+        view.setTagInternal(com.android.internal.R.id.remote_views_stable_id, stableId);
+    }
+
+    // Returns the next recyclable child of the view group, or -1 if there are none.
+    private static int getNextRecyclableChild(ViewGroup vg) {
+        Integer tag = (Integer) vg.getTag(com.android.internal.R.id.remote_views_next_child);
+        return tag == null ? -1 : tag;
+    }
+
+    private static int getViewLayoutId(View v) {
+        return (Integer) v.getTag(R.id.widget_frame);
+    }
+
+    private static void setNextRecyclableChild(ViewGroup vg, int nextChild, int numChildren) {
+        if (nextChild < 0 || nextChild >= numChildren) {
+            vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, -1);
+        } else {
+            vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, nextChild);
+        }
+    }
+
+    private void finalizeViewRecycling(ViewGroup root) {
+        // Remove any recyclable children that were not used. nextChild should either be -1 or point
+        // to the next recyclable child that hasn't been recycled.
+        int nextChild = getNextRecyclableChild(root);
+        if (nextChild >= 0 && nextChild < root.getChildCount()) {
+            root.removeViews(nextChild, root.getChildCount() - nextChild);
+        }
+        // Make sure on the next round, we don't try to recycle if removeAllViews is not called.
+        setNextRecyclableChild(root, -1, 0);
+        // Traverse the view tree.
+        for (int i = 0; i < root.getChildCount(); i++) {
+            View child = root.getChildAt(i);
+            if (child instanceof ViewGroup && !child.isRootNamespace()) {
+                finalizeViewRecycling((ViewGroup) child);
+            }
+        }
+    }
+
     /**
      * ViewGroup methods that are related to adding Views.
      */
     private class ViewGroupActionAdd extends Action {
+        static final int NO_ID = -1;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         private RemoteViews mNestedViews;
         private int mIndex;
+        private int mStableId;
 
         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews) {
-            this(viewId, nestedViews, -1 /* index */);
+            this(viewId, nestedViews, -1 /* index */, NO_ID /* nestedViewId */);
         }
 
         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index) {
+            this(viewId, nestedViews, index, NO_ID /* nestedViewId */);
+        }
+
+        ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) {
             this.viewId = viewId;
             mNestedViews = nestedViews;
             mIndex = index;
+            mStableId = stableId;
             if (nestedViews != null) {
                 configureRemoteViewsAsChild(nestedViews);
             }
@@ -2018,6 +2146,7 @@
                 int depth, Map<Class, Object> classCookies) {
             viewId = parcel.readInt();
             mIndex = parcel.readInt();
+            mStableId = parcel.readInt();
             mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
             mNestedViews.addFlags(mApplyFlags);
         }
@@ -2025,6 +2154,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(viewId);
             dest.writeInt(mIndex);
+            dest.writeInt(mStableId);
             mNestedViews.writeToParcel(dest, flags);
         }
 
@@ -2033,6 +2163,17 @@
             return mNestedViews.hasSameAppInfo(parentInfo);
         }
 
+        private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
+            for (int nextChild = getNextRecyclableChild(target); nextChild < target.getChildCount();
+                    nextChild++) {
+                View child = target.getChildAt(nextChild);
+                if (getStableId(child) == mStableId) {
+                    return nextChild;
+                }
+            }
+            return -1;
+        }
+
         @Override
         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
                 ColorResources colorResources) {
@@ -2043,10 +2184,45 @@
                 return;
             }
 
+            // If removeAllViews was called, this returns the next potential recycled view.
+            // If there are no more views to recycle (or removeAllViews was not called), this
+            // will return -1.
+            final int nextChild = getNextRecyclableChild(target);
+            RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+            if (nextChild >= 0 && mStableId != NO_ID) {
+                // At that point, the views starting at index nextChild are the ones recyclable but
+                // not yet recycled. All views added on that round of application are placed before.
+                // Find the next view with the same stable id, or -1.
+                int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
+                if (recycledViewIndex >= 0) {
+                    View child = target.getChildAt(recycledViewIndex);
+                    if (getViewLayoutId(child) == rvToApply.getLayoutId()) {
+                        if (nextChild < recycledViewIndex) {
+                            target.removeViews(nextChild, recycledViewIndex - nextChild);
+                        }
+                        setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
+                        rvToApply.reapply(context, child, handler, null /* size */, colorResources,
+                                false /* topLevel */);
+                        return;
+                    }
+                    // If we cannot recycle the views, we still remove all views in between to
+                    // avoid weird behaviors and insert the new view in place of the old one.
+                    target.removeViews(nextChild, recycledViewIndex - nextChild + 1);
+                }
+            }
+            // If we cannot recycle, insert the new view before the next recyclable child.
+
             // Inflate nested views and add as children
-            target.addView(
-                    mNestedViews.apply(context, target, handler, null /* size */, colorResources),
-                    mIndex);
+            View nestedView = rvToApply.apply(context, target, handler, null /* size */,
+                    colorResources);
+            if (mStableId != NO_ID) {
+                setStableId(nestedView, mStableId);
+            }
+            target.addView(nestedView, mIndex >= 0 ? mIndex : nextChild);
+            if (nextChild >= 0) {
+                // If we are at the end, there is no reason to try to recycle anymore
+                setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
+            }
         }
 
         @Override
@@ -2063,24 +2239,91 @@
 
             // Inflate nested views and perform all the async tasks for the child remoteView.
             final Context context = root.mRoot.getContext();
-            final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg,
-                    null /* listener */, handler, null /* size */, colorResources);
+
+            // If removeAllViews was called, this returns the next potential recycled view.
+            // If there are no more views to recycle (or removeAllViews was not called), this
+            // will return -1.
+            final int nextChild = getNextRecyclableChild(targetVg);
+            if (nextChild >= 0 && mStableId != NO_ID) {
+                RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+                final int recycledViewIndex = target.findChildIndex(nextChild,
+                        view -> getStableId(view) == mStableId);
+                if (recycledViewIndex >= 0) {
+                    // At that point, the views starting at index nextChild are the ones
+                    // recyclable but not yet recycled. All views added on that round of
+                    // application are placed before.
+                    ViewTree recycled = target.mChildren.get(recycledViewIndex);
+                    // We can only recycle the view if the layout id is the same.
+                    if (getViewLayoutId(recycled.mRoot) == rvToApply.getLayoutId()) {
+                        if (recycledViewIndex > nextChild) {
+                            target.removeChildren(nextChild, recycledViewIndex - nextChild);
+                        }
+                        setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
+                        final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
+                                context,
+                                targetVg, null /* listener */, handler, null /* size */,
+                                colorResources,
+                                recycled.mRoot);
+                        final ViewTree tree = reapplyTask.doInBackground();
+                        if (tree == null) {
+                            throw new ActionException(reapplyTask.mError);
+                        }
+                        return new RuntimeAction() {
+                            @Override
+                            public void apply(View root, ViewGroup rootParent,
+                                    InteractionHandler handler, ColorResources colorResources)
+                                    throws ActionException {
+                                reapplyTask.onPostExecute(tree);
+                                if (recycledViewIndex > nextChild) {
+                                    targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
+                                }
+                            }
+                        };
+                    }
+                    // If the layout id is different, still remove the children as if we recycled
+                    // the view, to insert at the same place.
+                    target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
+                    return insertNewView(context, target, handler, colorResources,
+                            () -> targetVg.removeViews(nextChild,
+                                    recycledViewIndex - nextChild + 1));
+
+                }
+            }
+            // If we cannot recycle, simply add the view at the same available slot.
+            return insertNewView(context, target, handler, colorResources, () -> {});
+        }
+
+        private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
+                ColorResources colorResources, Runnable finalizeAction) {
+            ViewGroup targetVg = (ViewGroup) target.mRoot;
+            int nextChild = getNextRecyclableChild(targetVg);
+            final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
+                    null /* listener */, handler, null /* size */, colorResources,
+                    null /* result */);
             final ViewTree tree = task.doInBackground();
 
             if (tree == null) {
                 throw new ActionException(task.mError);
             }
+            if (mStableId != NO_ID) {
+                setStableId(task.mResult, mStableId);
+            }
 
             // Update the global view tree, so that next call to findViewTreeById
             // goes through the subtree as well.
-            target.addChild(tree, mIndex);
+            final int insertIndex = mIndex >= 0 ? mIndex : nextChild;
+            target.addChild(tree, insertIndex);
+            if (nextChild >= 0) {
+                setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
+            }
 
             return new RuntimeAction() {
                 @Override
                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
                         ColorResources colorResources) throws ActionException {
                     task.onPostExecute(tree);
-                    targetVg.addView(task.mResult, mIndex);
+                    finalizeAction.run();
+                    targetVg.addView(task.mResult, insertIndex);
                 }
             };
         }
@@ -2148,7 +2391,14 @@
             }
 
             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
-                target.removeAllViews();
+                // Remote any view without a stable id
+                for (int i = target.getChildCount() - 1; i >= 0; i--) {
+                    if (!hasStableId(target.getChildAt(i))) {
+                        target.removeViewAt(i);
+                    }
+                }
+                // In the end, only children with a stable id (i.e. recyclable) are left.
+                setNextRecyclableChild(target, 0, target.getChildCount());
                 return;
             }
 
@@ -2170,8 +2420,8 @@
             final ViewGroup targetVg = (ViewGroup) target.mRoot;
 
             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
-                // Clear all children when there's no excepted view
-                target.mChildren = null;
+                target.mChildren.removeIf(childTree -> !hasStableId(childTree.mRoot));
+                setNextRecyclableChild(targetVg, 0, target.mChildren.size());
             } else {
                 // Remove just the children which don't match the excepted view
                 target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
@@ -2184,7 +2434,11 @@
                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
                         ColorResources colorResources) throws ActionException {
                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
-                        targetVg.removeAllViews();
+                        for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
+                            if (!hasStableId(targetVg.getChildAt(i))) {
+                                targetVg.removeViewAt(i);
+                            }
+                        }
                         return;
                     }
 
@@ -3084,6 +3338,7 @@
         }
         mApplication = portrait.mApplication;
         mLayoutId = portrait.mLayoutId;
+        mViewId = portrait.mViewId;
         mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
 
         mLandscape = landscape;
@@ -3136,6 +3391,7 @@
         RemoteViews smallestView = findSmallestRemoteView();
         mApplication = smallestView.mApplication;
         mLayoutId = smallestView.mLayoutId;
+        mViewId = smallestView.mViewId;
         mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
     }
 
@@ -3253,6 +3509,7 @@
                     ApplicationInfo.CREATOR.createFromParcel(parcel);
             mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
             mLayoutId = parcel.readInt();
+            mViewId = parcel.readInt();
             mLightBackgroundLayoutId = parcel.readInt();
 
             readActionsFromParcel(parcel, depth);
@@ -3273,6 +3530,7 @@
             RemoteViews smallestView = findSmallestRemoteView();
             mApplication = smallestView.mApplication;
             mLayoutId = smallestView.mLayoutId;
+            mViewId = smallestView.mViewId;
             mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
         } else {
             // MODE_HAS_LANDSCAPE_AND_PORTRAIT
@@ -3281,6 +3539,7 @@
                     mClassCookies);
             mApplication = mPortrait.mApplication;
             mLayoutId = mPortrait.mLayoutId;
+            mViewId = mPortrait.mViewId;
             mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
         }
         mApplyFlags = parcel.readInt();
@@ -3354,6 +3613,8 @@
                 return new SetOnCheckedChangeResponse(parcel);
             case NIGHT_MODE_REFLECTION_ACTION_TAG:
                 return new NightModeReflectionAction(parcel);
+            case SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG:
+                return new SetRemoteCollectionItemListAdapterAction(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
@@ -3458,6 +3719,29 @@
     }
 
     /**
+     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the given
+     * {@link RemoteViews}. If the {@link RemoteViews} may be re-inflated or updated,
+     * {@link #removeAllViews(int)} must be called on the same {@code viewId
+     * } before the first call to this method for the behavior of this method to be predictable.
+     *
+     * The {@code stableId} will be used to identify a potential view to recycled when the remote
+     * view is inflated. Views can be re-used if inserted in the same order, potentially with
+     * some views appearing / disappearing.
+     *
+     * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
+     * are not reset, so what was applied in previous round will have an effect. As a view may be
+     * re-created at any time by the host, the RemoteViews should not rely on keeping information
+     * from previous applications and always re-set all the properties they need.
+     *
+     * @param viewId The id of the parent {@link ViewGroup} to add child into.
+     * @param nestedView {@link RemoteViews} that describes the child.
+     * @param stableId An id that is stable across different versions of RemoteViews.
+     */
+    public void addStableView(@IdRes int viewId, @NonNull RemoteViews nestedView, int stableId) {
+        addAction(new ViewGroupActionAdd(viewId, nestedView, -1 /* index */, stableId));
+    }
+
+    /**
      * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
      * given {@link RemoteViews}.
      *
@@ -4003,6 +4287,25 @@
     }
 
     /**
+     * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
+     * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
+     * This is a simpler but less flexible approach to populating collection widgets. Its use is
+     * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
+     * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
+     * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
+     * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
+     *
+     * This API is supported in the compatibility library for previous API levels, see
+     * RemoteViewsCompat.
+     *
+     * @param viewId The id of the {@link AdapterView}.
+     * @param items The items to display in the {@link AdapterView}.
+     */
+    public void setRemoteAdapter(@IdRes int viewId, @NonNull RemoteCollectionItems items) {
+        addAction(new SetRemoteCollectionItemListAdapterAction(viewId, items));
+    }
+
+    /**
      * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
      *
      * @param viewId The id of the view to change
@@ -4870,23 +5173,24 @@
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
             SizeF size) {
-        return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
-                .startTaskOnExecutor(executor);
+        return applyAsync(context, parent, executor, listener, handler, size,
+                null /* themeColors */);
     }
 
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
-        return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
-                .startTaskOnExecutor(executor);
+        return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
+                handler, colorResources, null /* result */,
+                true /* topLevel */).startTaskOnExecutor(executor);
     }
 
-    private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
+    private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
-            ColorResources colorResources) {
+            ColorResources colorResources, View result) {
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
-                handler, colorResources, null /* result */);
+                handler, colorResources, result, false /* topLevel */);
     }
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -4898,6 +5202,12 @@
         final OnViewAppliedListener mListener;
         final InteractionHandler mHandler;
         final ColorResources mColorResources;
+        /**
+         * Whether the remote view is the top-level one (i.e. not within an action).
+         *
+         * This is only used if the result is specified (i.e. the view is being recycled).
+         */
+        final boolean mTopLevel;
 
         private View mResult;
         private ViewTree mTree;
@@ -4906,13 +5216,15 @@
 
         private AsyncApplyTask(
                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
-                InteractionHandler handler, ColorResources colorResources, View result) {
+                InteractionHandler handler, ColorResources colorResources,
+                View result, boolean topLevel) {
             mRV = rv;
             mParent = parent;
             mContext = context;
             mListener = listener;
             mColorResources = colorResources;
             mHandler = handler;
+            mTopLevel = topLevel;
 
             mResult = result;
         }
@@ -4959,6 +5271,10 @@
                             a.apply(viewTree.mRoot, mParent, handler, mColorResources);
                         }
                     }
+                    // If the parent of the view is has is a root, resolve the recycling.
+                    if (mTopLevel && mResult instanceof ViewGroup) {
+                        finalizeViewRecycling((ViewGroup) mResult);
+                    }
                 } catch (Exception e) {
                     mError = e;
                 }
@@ -5011,6 +5327,14 @@
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
+        reapply(context, v, handler, size, colorResources, true);
+    }
+
+    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+    // should set it to false.
+    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
+            ColorResources colorResources, boolean topLevel) {
+
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         // In the case that a view has this RemoteViews applied in one orientation or size, is
@@ -5025,6 +5349,11 @@
         }
 
         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
+
+        // If the parent of the view is has is a root, resolve the recycling.
+        if (topLevel && v instanceof ViewGroup) {
+            finalizeViewRecycling((ViewGroup) v);
+        }
     }
 
     /**
@@ -5068,8 +5397,8 @@
         }
 
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, colorResources, v).startTaskOnExecutor(
-                executor);
+                context, listener, handler, colorResources, v, true /* topLevel */)
+                .startTaskOnExecutor(executor);
     }
 
     private void performApply(View v, ViewGroup parent, InteractionHandler handler,
@@ -5282,6 +5611,7 @@
                 mIdealSize.writeToParcel(dest, flags);
             }
             dest.writeInt(mLayoutId);
+            dest.writeInt(mViewId);
             dest.writeInt(mLightBackgroundLayoutId);
             writeActionsToParcel(dest);
         } else if (hasSizedRemoteViews()) {
@@ -5470,6 +5800,14 @@
             mChildren.add(index, child);
         }
 
+        public void removeChildren(int start, int count) {
+            if (mChildren != null) {
+                for (int i = 0; i < count; i++) {
+                    mChildren.remove(start);
+                }
+            }
+        }
+
         private void addViewChild(View v) {
             // ViewTree only contains Views which can be found using findViewById.
             // If isRootNamespace is true, this view is skipped.
@@ -5500,6 +5838,28 @@
                 }
             }
         }
+
+        /** Find the first child for which the condition is true and return its index. */
+        public int findChildIndex(Predicate<View> condition) {
+            return findChildIndex(0, condition);
+        }
+
+        /**
+         * Find the first child, starting at {@code startIndex}, for which the condition is true and
+         * return its index.
+         */
+        public int findChildIndex(int startIndex, Predicate<View> condition) {
+            if (mChildren == null) {
+                return -1;
+            }
+
+            for (int i = startIndex; i < mChildren.size(); i++) {
+                if (condition.test(mChildren.get(i).mRoot)) {
+                    return i;
+                }
+            }
+            return -1;
+        }
     }
 
     /**
@@ -5757,10 +6117,207 @@
         return true;
     }
 
+    /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
+    public static final class RemoteCollectionItems implements Parcelable {
+        private final long[] mIds;
+        private final RemoteViews[] mViews;
+        private final boolean mHasStableIds;
+        private final int mViewTypeCount;
+
+        RemoteCollectionItems(
+                long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
+            mIds = ids;
+            mViews = views;
+            mHasStableIds = hasStableIds;
+            mViewTypeCount = viewTypeCount;
+            if (ids.length != views.length) {
+                throw new IllegalArgumentException(
+                        "RemoteCollectionItems has different number of ids and views");
+            }
+            if (viewTypeCount < 1) {
+                throw new IllegalArgumentException("View type count must be >= 1");
+            }
+            int layoutIdCount = (int) Arrays.stream(views)
+                    .mapToInt(RemoteViews::getLayoutId)
+                    .distinct()
+                    .count();
+            if (layoutIdCount > viewTypeCount) {
+                throw new IllegalArgumentException(
+                        "View type count is set to " + viewTypeCount + ", but the collection "
+                                + "contains " + layoutIdCount + " different layout ids");
+            }
+        }
+
+        RemoteCollectionItems(Parcel in) {
+            int length = in.readInt();
+            mIds = new long[length];
+            in.readLongArray(mIds);
+            mViews = new RemoteViews[length];
+            in.readTypedArray(mViews, RemoteViews.CREATOR);
+            mHasStableIds = in.readBoolean();
+            mViewTypeCount = in.readInt();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mIds.length);
+            dest.writeLongArray(mIds);
+            dest.writeTypedArray(mViews, flags);
+            dest.writeBoolean(mHasStableIds);
+            dest.writeInt(mViewTypeCount);
+        }
+
+        /**
+         * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id
+         * should be considered meaningful across collection updates.
+         *
+         * @return Id for the position.
+         */
+        public long getItemId(int position) {
+            return mIds[position];
+        }
+
+        /**
+         * Returns the {@link RemoteViews} to display at {@code position}.
+         *
+         * @return RemoteViews for the position.
+         */
+        @NonNull
+        public RemoteViews getItemView(int position) {
+            return mViews[position];
+        }
+
+        /**
+         * Returns the number of elements in the collection.
+         *
+         * @return Count of items.
+         */
+        public int getItemCount() {
+            return mIds.length;
+        }
+
+        /**
+         * Returns the view type count for the collection when used in an adapter
+         *
+         * @return Count of view types for the collection when used in an adapter.
+         * @see android.widget.Adapter#getViewTypeCount()
+         */
+        public int getViewTypeCount() {
+            return mViewTypeCount;
+        }
+
+        /**
+         * Indicates whether the item ids are stable across changes to the underlying data.
+         *
+         * @return True if the same id always refers to the same object.
+         * @see android.widget.Adapter#hasStableIds()
+         */
+        public boolean hasStableIds() {
+            return mHasStableIds;
+        }
+
+        @NonNull
+        public static final Creator<RemoteCollectionItems> CREATOR =
+                new Creator<RemoteCollectionItems>() {
+            @NonNull
+            @Override
+            public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
+                return new RemoteCollectionItems(source);
+            }
+
+            @NonNull
+            @Override
+            public RemoteCollectionItems[] newArray(int size) {
+                return new RemoteCollectionItems[size];
+            }
+        };
+
+        /** Builder class for {@link RemoteCollectionItems} objects.*/
+        public static final class Builder {
+            private final LongArray mIds = new LongArray();
+            private final List<RemoteViews> mViews = new ArrayList<>();
+            private boolean mHasStableIds;
+            private int mViewTypeCount;
+
+            /**
+             * Adds a {@link RemoteViews} to the collection.
+             *
+             * @param id Id to associate with the row. Use {@link #setHasStableIds(boolean)} to
+             *           indicate that ids are stable across changes to the collection.
+             * @param view RemoteViews to display for the row.
+             */
+            @NonNull
+            // Covered by getItemId, getItemView, getItemCount.
+            @SuppressLint("MissingGetterMatchingBuilder")
+            public Builder addItem(long id, @NonNull RemoteViews view) {
+                if (view == null) throw new NullPointerException();
+                if (view.hasMultipleLayouts()) {
+                    throw new IllegalArgumentException(
+                            "RemoteViews used in a RemoteCollectionItems cannot specify separate "
+                                    + "layouts for orientations or sizes.");
+                }
+                mIds.add(id);
+                mViews.add(view);
+                return this;
+            }
+
+            /**
+             * Sets whether the item ids are stable across changes to the underlying data.
+             *
+             * @see android.widget.Adapter#hasStableIds()
+             */
+            @NonNull
+            public Builder setHasStableIds(boolean hasStableIds) {
+                mHasStableIds = hasStableIds;
+                return this;
+            }
+
+            /**
+             * Sets the view type count for the collection when used in an adapter. This can be set
+             * to the maximum number of different layout ids that will be used by RemoteViews in
+             * this collection.
+             *
+             * If this value is not set, then a value will be inferred from the provided items. As
+             * a result, the adapter may need to be recreated when the list is updated with
+             * previously unseen RemoteViews layouts for new items.
+             *
+             * @see android.widget.Adapter#getViewTypeCount()
+             */
+            @NonNull
+            public Builder setViewTypeCount(int viewTypeCount) {
+                mViewTypeCount = viewTypeCount;
+                return this;
+            }
+
+            /** Creates the {@link RemoteCollectionItems} defined by this builder. */
+            @NonNull
+            public RemoteCollectionItems build() {
+                if (mViewTypeCount < 1) {
+                    // If a view type count wasn't specified, set it to be the number of distinct
+                    // layout ids used in the items.
+                    mViewTypeCount = (int) mViews.stream()
+                            .mapToInt(RemoteViews::getLayoutId)
+                            .distinct()
+                            .count();
+                }
+                return new RemoteCollectionItems(
+                        mIds.toArray(),
+                        mViews.toArray(new RemoteViews[0]),
+                        mHasStableIds,
+                        Math.max(mViewTypeCount, 1));
+            }
+        }
+    }
+
     /**
-     *  Set the ID of the top-level view of the XML layout.
+     * Set the ID of the top-level view of the XML layout.
      *
-     *  Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
+     * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
      *
      * @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
      * term of other RemoteViews (e.g. {@link #RemoteViews(RemoteViews, RemoteViews)}).
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index d59a415..2464b4a 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -26,6 +26,7 @@
 import android.text.style.SuggestionSpan;
 import android.util.Log;
 import android.util.LruCache;
+import android.util.Range;
 import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SpellCheckerSession;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
@@ -62,7 +63,8 @@
     // Pause between each spell check to keep the UI smooth
     private final static int SPELL_PAUSE_DURATION = 400; // milliseconds
 
-    private static final int MIN_SENTENCE_LENGTH = 50;
+    // The maximum length of sentence.
+    private static final int MAX_SENTENCE_LENGTH = WORD_ITERATOR_INTERVAL;
 
     private static final int USE_SPAN_RANGE = -1;
 
@@ -89,7 +91,7 @@
 
     // Shared by all SpellParsers. Cannot be shared with TextView since it may be used
     // concurrently due to the asynchronous nature of onGetSuggestions.
-    private WordIterator mWordIterator;
+    private SentenceIteratorWrapper mSentenceIterator;
 
     @Nullable
     private TextServicesManager mTextServicesManager;
@@ -151,8 +153,9 @@
         resetSession();
 
         if (locale != null) {
-            // Change SpellParsers' wordIterator locale
-            mWordIterator = new WordIterator(locale);
+            // Change SpellParsers' sentenceIterator locale
+            mSentenceIterator = new SentenceIteratorWrapper(
+                    BreakIterator.getSentenceInstance(locale));
         }
 
         // This class is the listener for locale change: warn other locale-aware objects
@@ -306,22 +309,30 @@
             final int start = editable.getSpanStart(spellCheckSpan);
             final int end = editable.getSpanEnd(spellCheckSpan);
 
-            // Do not check this word if the user is currently editing it
-            final boolean isEditing;
+            // Check the span if any of following conditions is met:
+            // - the user is not currently editing it
+            // - or `forceCheckWhenEditingWord` is true.
+            final boolean isNotEditing;
 
             // Defer spell check when typing a word ending with a punctuation like an apostrophe
             // which could end up being a mid-word punctuation.
             if (selectionStart == end + 1
                     && WordIterator.isMidWordPunctuation(
                             mCurrentLocale, Character.codePointBefore(editable, end + 1))) {
-                isEditing = false;
-            } else {
+                isNotEditing = false;
+            } else if (selectionEnd <= start || selectionStart > end) {
                 // Allow the overlap of the cursor and the first boundary of the spell check span
                 // no to skip the spell check of the following word because the
                 // following word will never be spell-checked even if the user finishes composing
-                isEditing = selectionEnd <= start || selectionStart > end;
+                isNotEditing = true;
+            } else {
+                // When cursor is at the end of spell check span, allow spell check if the
+                // character before cursor is a separator.
+                isNotEditing = selectionStart == end
+                        && selectionStart > 0
+                        && isSeparator(Character.codePointBefore(editable, selectionStart));
             }
-            if (start >= 0 && end > start && (forceCheckWhenEditingWord || isEditing)) {
+            if (start >= 0 && end > start && (forceCheckWhenEditingWord || isNotEditing)) {
                 spellCheckSpan.setSpellCheckInProgress(true);
                 final TextInfo textInfo = new TextInfo(editable, start, end, mCookie, mIds[i]);
                 textInfos[textInfosCount++] = textInfo;
@@ -346,6 +357,19 @@
         }
     }
 
+    private static boolean isSeparator(int codepoint) {
+        final int type = Character.getType(codepoint);
+        return ((1 << type) & ((1 << Character.SPACE_SEPARATOR)
+                | (1 << Character.LINE_SEPARATOR)
+                | (1 << Character.PARAGRAPH_SEPARATOR)
+                | (1 << Character.DASH_PUNCTUATION)
+                | (1 << Character.END_PUNCTUATION)
+                | (1 << Character.FINAL_QUOTE_PUNCTUATION)
+                | (1 << Character.INITIAL_QUOTE_PUNCTUATION)
+                | (1 << Character.START_PUNCTUATION)
+                | (1 << Character.OTHER_PUNCTUATION))) != 0;
+    }
+
     private SpellCheckSpan onGetSuggestionsInternal(
             SuggestionsInfo suggestionsInfo, int offset, int length) {
         if (suggestionsInfo == null || suggestionsInfo.getCookie() != mCookie) {
@@ -534,6 +558,60 @@
         mTextView.invalidateRegion(start, end, false /* No cursor involved */);
     }
 
+    /**
+     * A wrapper of sentence iterator which only processes the specified window of the given text.
+     */
+    private static class SentenceIteratorWrapper {
+        private BreakIterator mSentenceIterator;
+        private int mStartOffset;
+        private int mEndOffset;
+
+        SentenceIteratorWrapper(BreakIterator sentenceIterator) {
+            mSentenceIterator = sentenceIterator;
+        }
+
+        /**
+         * Set the char sequence and the text window to process.
+         */
+        public void setCharSequence(CharSequence sequence, int start, int end) {
+            mStartOffset = Math.max(0, start);
+            mEndOffset = Math.min(end, sequence.length());
+            mSentenceIterator.setText(sequence.subSequence(mStartOffset, mEndOffset).toString());
+        }
+
+        /**
+         * See {@link BreakIterator#preceding(int)}
+         */
+        public int preceding(int offset) {
+            if (offset < mStartOffset) {
+                return BreakIterator.DONE;
+            }
+            int result = mSentenceIterator.preceding(offset - mStartOffset);
+            return result == BreakIterator.DONE ? BreakIterator.DONE : result + mStartOffset;
+        }
+
+        /**
+         * See {@link BreakIterator#following(int)}
+         */
+        public int following(int offset) {
+            if (offset > mEndOffset) {
+                return BreakIterator.DONE;
+            }
+            int result = mSentenceIterator.following(offset - mStartOffset);
+            return result == BreakIterator.DONE ? BreakIterator.DONE : result + mStartOffset;
+        }
+
+        /**
+         * See {@link BreakIterator#isBoundary(int)}
+         */
+        public boolean isBoundary(int offset) {
+            if (offset < mStartOffset || offset > mEndOffset) {
+                return false;
+            }
+            return mSentenceIterator.isBoundary(offset - mStartOffset);
+        }
+    }
+
     private class SpellParser {
         private Object mRange = new Object();
 
@@ -582,27 +660,15 @@
 
         public void parse() {
             Editable editable = (Editable) mTextView.getText();
-            // Iterate over the newly added text and schedule new SpellCheckSpans
-            final int start =  Math.max(
-                    0, editable.getSpanStart(mRange) - MIN_SENTENCE_LENGTH);
+            final int textChangeStart = editable.getSpanStart(mRange);
+            final int textChangeEnd = editable.getSpanEnd(mRange);
 
-            final int end = editable.getSpanEnd(mRange);
+            Range<Integer> sentenceBoundary = detectSentenceBoundary(editable, textChangeStart,
+                    textChangeEnd);
+            int sentenceStart = sentenceBoundary.getLower();
+            int sentenceEnd = sentenceBoundary.getUpper();
 
-            int wordIteratorWindowEnd = Math.min(end, start + WORD_ITERATOR_INTERVAL);
-            mWordIterator.setCharSequence(editable, start, wordIteratorWindowEnd);
-
-            // Move back to the beginning of the current word, if any
-            int wordStart = mWordIterator.preceding(start);
-            int wordEnd;
-            if (wordStart == BreakIterator.DONE) {
-                wordEnd = mWordIterator.following(start);
-                if (wordEnd != BreakIterator.DONE) {
-                    wordStart = mWordIterator.getBeginning(wordEnd);
-                }
-            } else {
-                wordEnd = mWordIterator.getEnd(wordStart);
-            }
-            if (wordEnd == BreakIterator.DONE) {
+            if (sentenceStart == sentenceEnd) {
                 if (DBG) {
                     Log.i(TAG, "No more spell check.");
                 }
@@ -612,29 +678,16 @@
 
             boolean scheduleOtherSpellCheck = false;
 
-            if (wordIteratorWindowEnd < end) {
+            if (sentenceEnd < textChangeEnd) {
                 if (DBG) {
                     Log.i(TAG, "schedule other spell check.");
                 }
                 // Several batches needed on that region. Cut after last previous word
                 scheduleOtherSpellCheck = true;
             }
-            int spellCheckEnd = mWordIterator.preceding(wordIteratorWindowEnd);
-            boolean correct = spellCheckEnd != BreakIterator.DONE;
-            if (correct) {
-                spellCheckEnd = mWordIterator.getEnd(spellCheckEnd);
-                correct = spellCheckEnd != BreakIterator.DONE;
-            }
-            if (!correct) {
-                if (DBG) {
-                    Log.i(TAG, "Incorrect range span.");
-                }
-                stop();
-                return;
-            }
+            int spellCheckEnd = sentenceEnd;
             do {
-                // TODO: Find the start position of the sentence.
-                int spellCheckStart = wordStart;
+                int spellCheckStart = sentenceStart;
                 boolean createSpellCheckSpan = true;
                 // Cancel or merge overlapped spell check spans
                 for (int i = 0; i < mLength; ++i) {
@@ -671,27 +724,23 @@
                 }
 
                 // Stop spell checking when there are no characters in the range.
-                if (spellCheckEnd < start) {
-                    break;
-                }
                 if (spellCheckEnd <= spellCheckStart) {
                     Log.w(TAG, "Trying to spellcheck invalid region, from "
-                            + start + " to " + end);
+                            + sentenceStart + " to " + spellCheckEnd);
                     break;
                 }
                 if (createSpellCheckSpan) {
                     addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd);
                 }
             } while (false);
-            wordStart = spellCheckEnd;
-
-            if (scheduleOtherSpellCheck && wordStart != BreakIterator.DONE && wordStart <= end) {
+            sentenceStart = spellCheckEnd;
+            if (scheduleOtherSpellCheck && sentenceStart != BreakIterator.DONE
+                    && sentenceStart <= textChangeEnd) {
                 // Update range span: start new spell check from last wordStart
-                setRangeSpan(editable, wordStart, end);
+                setRangeSpan(editable, sentenceStart, textChangeEnd);
             } else {
                 removeRangeSpan(editable);
             }
-
             spellCheck(mForceCheckWhenEditingWord);
         }
 
@@ -708,6 +757,94 @@
         }
     }
 
+    private Range<Integer> detectSentenceBoundary(CharSequence sequence,
+            int textChangeStart, int textChangeEnd) {
+        // Only process a substring of the full text due to performance concern.
+        final int iteratorWindowStart = findSeparator(sequence,
+                Math.max(0, textChangeStart - MAX_SENTENCE_LENGTH),
+                Math.max(0, textChangeStart - 2 * MAX_SENTENCE_LENGTH));
+        final int iteratorWindowEnd = findSeparator(sequence,
+                Math.min(textChangeStart + 2 * MAX_SENTENCE_LENGTH, textChangeEnd),
+                Math.min(textChangeStart + 3 * MAX_SENTENCE_LENGTH, sequence.length()));
+        if (DBG) {
+            Log.d(TAG, "Set iterator window as [" + iteratorWindowStart + ", " + iteratorWindowEnd
+                    + ").");
+        }
+        mSentenceIterator.setCharSequence(sequence, iteratorWindowStart, iteratorWindowEnd);
+
+        // Detect the offset of sentence begin/end on the substring.
+        int sentenceStart = mSentenceIterator.isBoundary(textChangeStart) ? textChangeStart
+                : mSentenceIterator.preceding(textChangeStart);
+        int sentenceEnd = mSentenceIterator.following(sentenceStart);
+        if (sentenceEnd == BreakIterator.DONE) {
+            sentenceEnd = iteratorWindowEnd;
+        }
+        if (DBG) {
+            if (sentenceStart != sentenceEnd) {
+                Log.d(TAG, "Sentence detected [" + sentenceStart + ", " + sentenceEnd + ").");
+            }
+        }
+
+        if (sentenceEnd - sentenceStart <= MAX_SENTENCE_LENGTH) {
+            // Add more sentences until the MAX_SENTENCE_LENGTH limitation is reached.
+            while (sentenceEnd < textChangeEnd) {
+                int nextEnd = mSentenceIterator.following(sentenceEnd);
+                if (nextEnd == BreakIterator.DONE
+                        || nextEnd - sentenceStart > MAX_SENTENCE_LENGTH) {
+                    break;
+                }
+                sentenceEnd = nextEnd;
+            }
+        } else {
+            // If the sentence containing `textChangeStart` is longer than MAX_SENTENCE_LENGTH,
+            // the sentence will be sliced into sub-sentences of about MAX_SENTENCE_LENGTH
+            // characters each. This is done by processing the unchecked part of that sentence :
+            //   [textChangeStart, sentenceEnd)
+            //
+            // - If the `uncheckedLength` is bigger than MAX_SENTENCE_LENGTH, then check the
+            //   [textChangeStart, textChangeStart + MAX_SENTENCE_LENGTH), and leave the rest
+            //   part for the next check.
+            //
+            // - If the `uncheckedLength` is smaller than or equal to MAX_SENTENCE_LENGTH,
+            //   then check [sentenceEnd - MAX_SENTENCE_LENGTH, sentenceEnd).
+            //
+            // The offset should be rounded up to word boundary.
+            int uncheckedLength = sentenceEnd - textChangeStart;
+            if (uncheckedLength > MAX_SENTENCE_LENGTH) {
+                sentenceEnd = findSeparator(sequence, sentenceStart + MAX_SENTENCE_LENGTH,
+                        sentenceEnd);
+                sentenceStart = roundUpToWordStart(sequence, textChangeStart, sentenceStart);
+            } else {
+                sentenceStart = roundUpToWordStart(sequence, sentenceEnd - MAX_SENTENCE_LENGTH,
+                        sentenceStart);
+            }
+        }
+        return new Range(sentenceStart, sentenceEnd);
+    }
+
+    private int roundUpToWordStart(CharSequence sequence, int position, int frontBoundary) {
+        if (isSeparator(sequence.charAt(position))) {
+            return position;
+        }
+        int separator = findSeparator(sequence, position, frontBoundary);
+        return separator != frontBoundary ? separator + 1 : frontBoundary;
+    }
+
+    /**
+     * Search the range [start, end) of sequence and returns the position of the first separator.
+     * If end is smaller than start, do a reverse search.
+     * Returns `end` if no separator is found.
+     */
+    private static int findSeparator(CharSequence sequence, int start, int end) {
+        final int step = start < end ? 1 : -1;
+        for (int i = start; i != end; i += step) {
+            if (isSeparator(sequence.charAt(i))) {
+                return i;
+            }
+        }
+        return end;
+    }
+
     public static boolean haveWordBoundariesChanged(final Editable editable, final int start,
             final int end, final int spanStart, final int spanEnd) {
         final boolean haveWordBoundariesChanged;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fdc66fc..dba7fa9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -195,6 +195,7 @@
 import android.view.textservice.SpellCheckerSubtype;
 import android.view.textservice.TextServicesManager;
 import android.view.translation.TranslationRequestValue;
+import android.view.translation.UiTranslationController;
 import android.view.translation.ViewTranslationRequest;
 import android.view.translation.ViewTranslationResponse;
 import android.widget.RemoteViews.RemoteView;
@@ -502,7 +503,7 @@
     private boolean mImeIsConsumingInput;
 
     // Whether cursor is visible without regard to {@link mImeConsumesInput}.
-    // {code true} is the default value.
+    // {@code true} is the default value.
     private boolean mCursorVisibleFromAttr = true;
 
     static class Drawables {
@@ -9316,7 +9317,7 @@
         }
 
         for (int i = 0; i < n; i++) {
-            max = Math.max(max, layout.getLineWidth(i));
+            max = Math.max(max, layout.getLineMax(i));
         }
 
         return (int) Math.ceil(max);
@@ -10570,6 +10571,17 @@
         return mEditor == null ? true : mEditor.mCursorVisible;
     }
 
+    /**
+     * @return whether cursor is visible without regard to {@code mImeIsConsumingInput}.
+     * {@code true} is the default value.
+     *
+     * @see #setCursorVisible(boolean)
+     * @hide
+     */
+    public boolean isCursorVisibleFromAttr() {
+        return mCursorVisibleFromAttr;
+    }
+
     private boolean canMarquee() {
         int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
         return width > 0 && (mLayout.getLineWidth(0) > width
@@ -13835,6 +13847,10 @@
     @Override
     public ViewTranslationRequest onCreateTranslationRequest() {
         if (mText == null || mText.length() == 0) {
+            // TODO(b/182433547): remove before S release
+            if (UiTranslationController.DEBUG) {
+                Log.w(LOG_TAG, "Cannot create translation request for the empty text.");
+            }
             return null;
         }
         // Not translate password, editable text and not important for translation
@@ -13842,6 +13858,11 @@
         //  text selection apis, not support in S.
         boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
         if (isTextEditable() || isPassword || isTextSelectable()) {
+            // TODO(b/182433547): remove before S release
+            if (UiTranslationController.DEBUG) {
+                Log.w(LOG_TAG, "Cannot create translation request. editable = " + isTextEditable()
+                        + ", isPassword = " + isPassword + ", selectable = " + isTextSelectable());
+            }
             return null;
         }
         // TODO(b/176488462): apply the view's important for translation property
@@ -13870,6 +13891,9 @@
         // Restore to original text content.
         if (mTranslationTransformation != null) {
             setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
+        } else {
+            // TODO(b/182433547): remove before S release
+            Log.w(LOG_TAG, "onPauseUiTranslation(): no translated text.");
         }
     }
 
@@ -13889,7 +13913,8 @@
         if (mTranslationTransformation != null) {
             setTransformationMethod(mTranslationTransformation);
         } else {
-            Log.w(LOG_TAG, "onResumeTranslatedText(): no translated text.");
+            // TODO(b/182433547): remove before S release
+            Log.w(LOG_TAG, "onRestoreUiTranslation(): no translated text.");
         }
     }
 
@@ -13910,6 +13935,9 @@
         if (mTranslationTransformation != null) {
             setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
             mTranslationTransformation = null;
+        } else {
+            // TODO(b/182433547): remove before S release
+            Log.w(LOG_TAG, "onFinishUiTranslation(): no translated text.");
         }
     }
 
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 8f541d0..3eb35c2 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -18,6 +18,7 @@
 
 import android.view.SurfaceControl;
 import android.app.ActivityManager;
+import android.graphics.Rect;
 import android.window.StartingWindowInfo;
 import android.window.WindowContainerToken;
 
@@ -38,8 +39,12 @@
 
     /**
      * Called when the Task want to remove the starting window.
+     * @param leash A persistent leash for the top window in this task.
+     * @param frame Window frame of the top window.
+     * @param playRevealAnimation Play vanish animation.
      */
-    void removeStartingWindow(int taskId);
+    void removeStartingWindow(int taskId, in SurfaceControl leash, in Rect frame,
+            in boolean playRevealAnimation);
 
     /**
      * Called when the Task want to copy the splash screen.
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 35ccfca..da445b8 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -46,6 +46,8 @@
 import com.android.internal.R;
 import com.android.internal.policy.DecorView;
 
+import java.util.function.Consumer;
+
 /**
  * <p>The view which allows an activity to customize its splash screen exit animation.</p>
  *
@@ -77,7 +79,8 @@
 
     private Animatable mAnimatableIcon;
     private ValueAnimator mAnimator;
-
+    private Runnable mAnimationFinishListener;
+    private Consumer<Canvas> mOnDrawCallback;
     // cache original window and status
     private Window mWindow;
     private boolean mDrawBarBackground;
@@ -85,7 +88,7 @@
     private int mNavigationBarColor;
 
     /**
-     * Internal builder to create a SplashScreenWindowView object.
+     * Internal builder to create a SplashScreenView object.
      * @hide
      */
     public static class Builder {
@@ -391,7 +394,7 @@
      * Get the initial background color of this view.
      * @hide
      */
-    @ColorInt int getInitBackgroundColor() {
+    public @ColorInt int getInitBackgroundColor() {
         return mInitBackgroundColor;
     }
 
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index d1c1e40..c7672dc 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -126,6 +126,12 @@
      */
     public int splashScreenThemeResId;
 
+    /**
+     * Is keyguard occluded on default display.
+     * @hide
+     */
+    public boolean isKeyguardOccluded = false;
+
     public StartingWindowInfo() {
 
     }
@@ -147,6 +153,7 @@
         dest.writeTypedObject(topOpaqueWindowLayoutParams, flags);
         dest.writeTypedObject(mainWindowLayoutParams, flags);
         dest.writeInt(splashScreenThemeResId);
+        dest.writeBoolean(isKeyguardOccluded);
     }
 
     void readFromParcel(@NonNull Parcel source) {
@@ -157,6 +164,7 @@
                 WindowManager.LayoutParams.CREATOR);
         mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
         splashScreenThemeResId = source.readInt();
+        isKeyguardOccluded = source.readBoolean();
     }
 
     @Override
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 04020ec..3340cf4 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,6 +24,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.SurfaceControl;
@@ -100,9 +101,14 @@
 
     /**
      * Called when the Task want to remove the starting window.
+     * @param leash A persistent leash for the top window in this task. Release it once exit
+     *              animation has finished.
+     * @param frame Window frame of the top window.
+     * @param playRevealAnimation Play vanish animation.
      */
     @BinderThread
-    public void removeStartingWindow(int taskId) {}
+    public void removeStartingWindow(int taskId, @Nullable SurfaceControl leash,
+            @Nullable Rect frame, boolean playRevealAnimation) {}
 
     /**
      * Called when the Task want to copy the splash screen.
@@ -217,15 +223,16 @@
 
     private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
         @Override
-
         public void addStartingWindow(StartingWindowInfo windowInfo,
                 IBinder appToken) {
             mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken));
         }
 
         @Override
-        public void removeStartingWindow(int taskId) {
-            mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId));
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
+            mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId, leash, frame,
+                    playRevealAnimation));
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 6cfd498..d4d8536 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1119,7 +1119,7 @@
 
             ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
                     Context.CLIPBOARD_SERVICE);
-            clipboardManager.setPrimaryClip(clipData);
+            clipboardManager.setPrimaryClipAsPackage(clipData, getReferrerPackageName());
             Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
 
             // Log share completion via copy
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index c1952c7..957e416 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -166,4 +166,15 @@
 
     /** {@hide} */
     boolean setChargingStateUpdateDelayMillis(int delay);
+
+    /** Exposed as a test API. */
+    void setChargerAcOnline(boolean online, boolean forceUpdate);
+    /** Exposed as a test API. */
+    void setBatteryLevel(int level, boolean forceUpdate);
+    /** Exposed as a test API. */
+    void unplugBattery(boolean forceUpdate);
+    /** Exposed as a test API. */
+    void resetBattery(boolean forceUpdate);
+    /** Exposed as a test API. */
+    void suspendBatteryInput();
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ccb980e..592f7c7 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -21,6 +21,7 @@
 import android.media.permission.Identity;
 import android.os.Bundle;
 import android.os.RemoteCallback;
+import android.os.SharedMemory;
 
 import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -225,14 +226,19 @@
             IBinder client);
 
     /**
-     * Sets hotword detection configuration.
+     * Set configuration and pass read-only data to hotword detection service.
      *
-     * Note: Currently it will trigger hotword detection service after calling this function when
-     * all conditions meet the requirements.
-     *
-     * @param options Config data.
-     * @return {@link VoiceInteractionService#HOTWORD_CONFIG_SUCCESS} in case of success,
-     * {@link VoiceInteractionService#HOTWORD_CONFIG_FAILURE} in case of failure.
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
      */
-    int setHotwordDetectionConfig(in Bundle options);
+    void setHotwordDetectionServiceConfig(in Bundle options, in SharedMemory sharedMemory);
+
+    /**
+     * Requests to shutdown hotword detection service.
+     */
+    void shutdownHotwordDetectionService();
 }
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 0b937fa..364db06 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -31,4 +31,14 @@
     public boolean isFinalBuild() {
         return "REL".equals(Build.VERSION.CODENAME);
     }
+
+    /**
+     * The current platform SDK version.
+     */
+    public int platformTargetSdk() {
+        if (isFinalBuild()) {
+            return Build.VERSION.SDK_INT;
+        }
+        return Build.VERSION_CODES.CUR_DEVELOPMENT;
+    }
 }
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index c0bbe50..e408be2 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -34,7 +34,8 @@
             DISABLED_NON_TARGET_SDK,
             DISABLED_TARGET_SDK_TOO_HIGH,
             DEFERRED_VERIFICATION,
-            LOGGING_ONLY_CHANGE
+            LOGGING_ONLY_CHANGE,
+            PLATFORM_TOO_OLD
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {
@@ -65,6 +66,10 @@
      * Change is marked as logging only, and cannot be toggled.
      */
     public static final int LOGGING_ONLY_CHANGE = 5;
+    /**
+     * Change is gated by a target sdk version newer than the current platform sdk version.
+     */
+    public static final int PLATFORM_TOO_OLD = 6;
 
     @State
     public final int state;
@@ -123,6 +128,11 @@
                 throw new SecurityException(String.format(
                         "Cannot override %1$d because it is marked as a logging-only change.",
                         changeId));
+            case PLATFORM_TOO_OLD:
+                throw new SecurityException(String.format(
+                        "Cannot override %1$d for %2$s because the change's targetSdk threshold "
+                                + "(%3$d) is above the platform sdk.",
+                        changeId, packageName, changeIdTargetSdk));
         }
     }
 
@@ -170,6 +180,8 @@
                 return "DEFERRED_VERIFICATION";
             case LOGGING_ONLY_CHANGE:
                 return "LOGGING_ONLY_CHANGE";
+            case PLATFORM_TOO_OLD:
+                return "PLATFORM_TOO_OLD";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ee98878..52801fa 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -474,6 +474,12 @@
     public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets";
 
 
+    /*
+     * (long) The duration that the home button must be pressed before triggering Assist
+     */
+    public static final String HOME_BUTTON_LONG_PRESS_DURATION_MS =
+            "home_button_long_press_duration_ms";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/graphics/palette/Mean.java b/core/java/com/android/internal/graphics/palette/Mean.java
index 894f91b..bde0363 100644
--- a/core/java/com/android/internal/graphics/palette/Mean.java
+++ b/core/java/com/android/internal/graphics/palette/Mean.java
@@ -22,20 +22,19 @@
  * Represents a centroid in Kmeans algorithms.
  */
 public class Mean {
-    private static final Random RANDOM = new Random(0);
-
     public float[] center;
 
     /**
      * Constructor.
      *
      * @param upperBound maximum value of a dimension in the space Kmeans is optimizing in
+     * @param random used to generate a random center
      */
-    Mean(int upperBound) {
+    Mean(int upperBound, Random random) {
         center =
                 new float[]{
-                        RANDOM.nextInt(upperBound + 1), RANDOM.nextInt(upperBound + 1),
-                        RANDOM.nextInt(upperBound + 1)
+                        random.nextInt(upperBound + 1), random.nextInt(upperBound + 1),
+                        random.nextInt(upperBound + 1)
                 };
     }
 
diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
index a87a34f..1d865c2 100644
--- a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
@@ -22,6 +22,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
 
 
@@ -57,13 +58,27 @@
         }
 
         if (maxColors > means.length) {
+            // Always initialize Random with the same seed. Ensures the results of quantization
+            // are consistent, even when random centroids are required.
+            Random random = new Random(0x42688);
             int randomMeansToCreate = maxColors - means.length;
             for (int i = 0; i < randomMeansToCreate; i++) {
-                mMeans[means.length + i] = new Mean(100);
+                mMeans[means.length + i] = new Mean(100, random);
             }
         }
 
         for (int pixel : pixels) {
+            // These are pixels from the bitmap that is being quantized.
+            // Depending on the bitmap & downscaling, it may have pixels that are less than opaque
+            // Ignore those pixels.
+            ///
+            // Note: they don't _have_ to be ignored, for example, we could instead turn them
+            // opaque. Traditionally, including outside Android, quantizers ignore transparent
+            // pixels, so that strategy was chosen.
+            int alpha = (pixel >> 24) & 0xff;
+            if (alpha < 255) {
+                continue;
+            }
             Integer currentCount = mCountByColor.get(pixel);
             if (currentCount == null) {
                 currentCount = 0;
@@ -105,8 +120,12 @@
     /** Create random starting centroids for K-means. */
     public static float[][] randomMeans(int maxColors, int upperBound) {
         float[][] means = new float[maxColors][];
+
+        // Always initialize Random with the same seed. Ensures the results of quantization
+        // are consistent, even when random centroids are required.
+        Random random = new Random(0x42688);
         for (int i = 0; i < maxColors; i++) {
-            means[i] = new Mean(upperBound).center;
+            means[i] = new Mean(upperBound, random).center;
         }
         return means;
     }
diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
index 66206bf..a2652ea 100644
--- a/core/java/com/android/internal/graphics/palette/WuQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
@@ -78,7 +78,12 @@
         // All of the sample Wu implementations are reimplementations of a snippet of C code from
         // the early 90s. They all cap the maximum # of colors at 256, and it is impossible to tell
         // if this is a requirement, a consequence of QUANT_SIZE, or arbitrary.
-        this.mMaxColors = Math.min(MAX_COLORS, maxColorCount);
+        //
+        // Also, the number of maximum colors should be capped at the number of pixels - otherwise,
+        // If extraction is run on a set of pixels whose count is less than max colors,
+        // then colors.length < max colors, and accesses to colors[index] throw an
+        // ArrayOutOfBoundsException.
+        this.mMaxColors = Math.min(Math.min(MAX_COLORS, maxColorCount), colors.length);
         Box[] cube = new Box[mMaxColors];
         int red, green, blue;
 
@@ -119,17 +124,13 @@
             }
         }
 
-        // 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) {
+        for (k = 0; k < mMaxColors; ++k) {
             weight = getVolume(cube[k], mWt);
             if (weight > 0) {
                 red = (int) (getVolume(cube[k], mMr) / weight);
                 green = (int) (getVolume(cube[k], mMg) / weight);
                 blue = (int) (getVolume(cube[k], mMb) / weight);
-                colors[k] = ((red & 0x0ff) << 16) | ((green & 0x0ff) << 8) | (blue & 0x0ff);
+                colors[k] = (255 << 24) | (red << 16) | (green << 8) | blue;
             } else {
                 colors[k] = 0;
             }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 5829047..7f0178e 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -206,9 +206,8 @@
      * Cancel the trace session of the CUJ.
      */
     public synchronized void cancel() {
-        // The session is ongoing, end the trace session.
-        // That means the cancel call is from external invocation, not from end().
-        if (mBeginVsyncId != INVALID_ID && mEndVsyncId == INVALID_ID) {
+        // We don't need to end the trace section if it never begun.
+        if (mBeginVsyncId != INVALID_ID) {
             Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
         }
         mCancelled = true;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9ecb0ad..11466f4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6976,6 +6976,11 @@
         return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
     }
 
+    @Override
+    public long getCpuMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+    }
+
     /**
      * Returns the consumption (in microcoulombs) that the given standard power bucket consumed.
      * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable
@@ -8482,6 +8487,11 @@
             return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
         }
 
+        @Override
+        public long getCpuMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+        }
+
         void initNetworkActivityLocked() {
             detachIfNotNull(mNetworkByteActivityCounters);
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 97f727b..b15543a 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -85,12 +85,14 @@
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
-            calculateApp(app, app.getBatteryStatsUid(), result);
+            calculateApp(app, app.getBatteryStatsUid(), query, result);
         }
     }
 
-    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
-        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
+    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+            BatteryUsageStatsQuery query, Result result) {
+        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED,
+                query.shouldForceUsePowerProfileModel(), result);
 
         app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
                 .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
@@ -112,7 +114,7 @@
     }
 
     private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
-        calculatePowerAndDuration(u, statsType, result);
+        calculatePowerAndDuration(u, statsType, false, result);
 
         app.cpuPowerMah = result.powerMah;
         app.cpuTimeMs = result.durationMs;
@@ -120,46 +122,16 @@
         app.packageWithHighestDrain = result.packageWithHighestDrain;
     }
 
-    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) {
+    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType,
+            boolean forceUsePowerProfileModel, Result result) {
         long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
 
-        // Constant battery drain when CPU is active
-        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
-
-        // Additional per-cluster battery drain
-        long[] cpuClusterTimes = u.getCpuClusterTimes();
-        if (cpuClusterTimes != null) {
-            if (cpuClusterTimes.length == mNumCpuClusters) {
-                for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
-                    double power = calculatePerCpuClusterPowerMah(cluster,
-                            cpuClusterTimes[cluster]);
-                    powerMah += power;
-                    if (DEBUG) {
-                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
-                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
-                                + " power=" + formatCharge(power));
-                    }
-                }
-            } else {
-                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
-                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
-            }
-        }
-
-        // Additional per-frequency battery drain
-        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
-            final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
-            for (int speed = 0; speed < speedsForCluster; speed++) {
-                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
-                final double power = calculatePerCpuFreqPowerMah(cluster, speed,
-                        timeUs / 1000);
-                if (DEBUG) {
-                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
-                            + speed + " timeUs=" + timeUs + " power="
-                            + formatCharge(power));
-                }
-                powerMah += power;
-            }
+        final double powerMah;
+        final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
+        if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) {
+            powerMah = calculateUidModeledPowerMah(u, statsType);
+        } else {
+            powerMah = uCtoMah(consumptionUC);
         }
 
         if (DEBUG && (durationMs != 0 || powerMah != 0)) {
@@ -208,6 +180,48 @@
         result.packageWithHighestDrain = packageWithHighestDrain;
     }
 
+    private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
+        // Constant battery drain when CPU is active
+        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
+
+        // Additional per-cluster battery drain
+        long[] cpuClusterTimes = u.getCpuClusterTimes();
+        if (cpuClusterTimes != null) {
+            if (cpuClusterTimes.length == mNumCpuClusters) {
+                for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+                    double power = calculatePerCpuClusterPowerMah(cluster,
+                            cpuClusterTimes[cluster]);
+                    powerMah += power;
+                    if (DEBUG) {
+                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
+                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
+                                + " power=" + formatCharge(power));
+                    }
+                }
+            } else {
+                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
+            }
+        }
+
+        // Additional per-frequency battery drain
+        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+            final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
+            for (int speed = 0; speed < speedsForCluster; speed++) {
+                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
+                final double power = calculatePerCpuFreqPowerMah(cluster, speed,
+                        timeUs / 1000);
+                if (DEBUG) {
+                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+                            + speed + " timeUs=" + timeUs + " power="
+                            + formatCharge(power));
+                }
+                powerMah += power;
+            }
+        }
+        return powerMah;
+    }
+
     /**
      * Calculates active CPU power consumption.
      *
diff --git a/core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java b/core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java
new file mode 100644
index 0000000..2ffff73
--- /dev/null
+++ b/core/java/com/android/internal/os/SelectedProcessCpuThreadReader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.Process;
+
+/**
+ * Reads CPU usage statistics about a selected process identified by its cmdline.
+ *
+ * Handles finding the pid for the process and delegates CPU usage reading from the eBPF map to
+ * KernelSingleProcessCpuThreadReader. Exactly one long-lived instance of the process is expected.
+ * Otherwise, no statistics are returned.
+ *
+ * See also SystemServerCpuThreadReader.
+ */
+public final class SelectedProcessCpuThreadReader {
+    private final String[] mCmdline;
+
+    private int mPid;
+    private KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
+
+    public SelectedProcessCpuThreadReader(String cmdline) {
+        mCmdline = new String[] { cmdline };
+    }
+
+    /** Returns CPU times, per thread group, since tracking started. */
+    @Nullable
+    public KernelSingleProcessCpuThreadReader.ProcessCpuUsage readAbsolute() {
+        int[] pids = Process.getPidsForCommands(mCmdline);
+        if (pids == null || pids.length != 1) {
+            return null;
+        }
+        int pid = pids[0];
+        if (mPid == pid) {
+            return mKernelCpuThreadReader.getProcessCpuUsage();
+        }
+        mPid = pid;
+        mKernelCpuThreadReader = KernelSingleProcessCpuThreadReader.create(mPid);
+        mKernelCpuThreadReader.startTrackingThreadCpuTimes();
+        return null;
+    }
+}
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
index bed85ae..83309fc 100644
--- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -359,18 +359,6 @@
                 + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
     }
 
-    /**
-     * Writes the log buffer to a new file for the bugreport.
-     *
-     * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
-     * {@link #stopProtoLog(PrintWriter, boolean)}.
-     */
-    public void writeProtoLogToFile() {
-        synchronized (mProtoLogEnabledLock) {
-            writeProtoLogToFileLocked();
-        }
-    }
-
     private void writeProtoLogToFileLocked() {
         try {
             long offset =
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 200e0dd..fea0751 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -257,4 +257,12 @@
      * file descriptor passed in.
      */
      void passThroughShellCommand(in String[] args, in ParcelFileDescriptor pfd);
+
+    /**
+     * Enables/disables the navigation bar luma sampling.
+     *
+     * @param displayId the id of the display to notify.
+     * @param enable {@code true} if enable, otherwise set to {@code false}.
+     */
+    void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 85114e5..b57b4b9 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -72,5 +72,5 @@
     void onBarringInfoChanged(in BarringInfo barringInfo);
     void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
     void onDataEnabledChanged(boolean enabled, int reason);
-    void onAllowedNetworkTypesChanged(in Map allowedNetworkTypeList);
+    void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 95e0a3b..83691ee 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -95,5 +95,5 @@
     void notifyPhysicalChannelConfigForSubscriber(in int subId,
             in List<PhysicalChannelConfig> configs);
     void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
-    void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList);
+    void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
 }
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index dc6880e..90c7282 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -175,7 +175,7 @@
             mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
             for (int action : ACTIONS_ALL) {
                 mTraceThresholdPerAction[action] =
-                    properties.getInt(getTraceTriggerNameForAction(action), -1);
+                    properties.getInt(getNameOfAction(STATSD_ACTION[action]), -1);
             }
         }
     }
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 19506a3..783d088 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -31,8 +31,10 @@
 import android.util.imetracing.InputConnectionHelper;
 import android.util.proto.ProtoOutputStream;
 import android.view.KeyEvent;
+import android.view.View;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DumpableInputConnection;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
@@ -49,7 +51,9 @@
 import com.android.internal.inputmethod.ISurroundingTextResultCallback;
 import com.android.internal.os.SomeArgs;
 
-public abstract class IInputConnectionWrapper extends IInputContext.Stub {
+import java.lang.ref.WeakReference;
+
+public final class IInputConnectionWrapper extends IInputContext.Stub {
     private static final String TAG = "IInputConnectionWrapper";
     private static final boolean DEBUG = false;
 
@@ -90,10 +94,13 @@
     private Looper mMainLooper;
     private Handler mH;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private Object mLock = new Object();
+    private final Object mLock = new Object();
     @GuardedBy("mLock")
     private boolean mFinished = false;
 
+    private final InputMethodManager mParentInputMethodManager;
+    private final WeakReference<View> mServedView;
+
     class MyHandler extends Handler {
         MyHandler(Looper looper) {
             super(looper);
@@ -104,11 +111,15 @@
             executeMessage(msg);
         }
     }
-    
-    public IInputConnectionWrapper(Looper mainLooper, @NonNull InputConnection inputConnection) {
+
+    public IInputConnectionWrapper(@NonNull Looper mainLooper,
+            @NonNull InputConnection inputConnection,
+            @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
         mInputConnection = inputConnection;
         mMainLooper = mainLooper;
         mH = new MyHandler(mMainLooper);
+        mParentInputMethodManager = inputMethodManager;
+        mServedView = new WeakReference<>(servedView);
     }
 
     @Nullable
@@ -118,21 +129,70 @@
         }
     }
 
-    protected Looper getLooper() {
-        synchronized (mMainLooper) {
-            return mMainLooper;
-        }
-    }
-
-    protected boolean isFinished() {
+    private boolean isFinished() {
         synchronized (mLock) {
             return mFinished;
         }
     }
 
-    protected abstract boolean isActive();
+    public boolean isActive() {
+        return mParentInputMethodManager.isActive() && !isFinished();
+    }
 
-    protected abstract InputMethodManager getIMM();
+    public View getServedView() {
+        return mServedView.get();
+    }
+
+    public void deactivate() {
+        if (isFinished()) {
+            // This is a small performance optimization.  Still only the 1st call of
+            // reportFinish() will take effect.
+            return;
+        }
+        closeConnection();
+
+        // Notify the app that the InputConnection was closed.
+        final View servedView = mServedView.get();
+        if (servedView != null) {
+            final Handler handler = servedView.getHandler();
+            // The handler is null if the view is already detached. When that's the case, for
+            // now, we simply don't dispatch this callback.
+            if (handler != null) {
+                if (DEBUG) {
+                    Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
+                }
+                if (handler.getLooper().isCurrentThread()) {
+                    servedView.onInputConnectionClosedInternal();
+                } else {
+                    handler.post(servedView::onInputConnectionClosedInternal);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "IInputConnectionWrapper{"
+                + "connection=" + getInputConnection()
+                + " finished=" + isFinished()
+                + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+                + " mServedView=" + mServedView.get()
+                + "}";
+    }
+
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        synchronized (mLock) {
+            // Check that the call is initiated in the main thread of the current InputConnection
+            // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+            // executed on this thread. Otherwise the messages are dispatched to the correct thread
+            // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
+            // reasons.
+            if ((mInputConnection instanceof DumpableInputConnection)
+                    && Looper.myLooper() == mMainLooper) {
+                ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
+            }
+        }
+    }
 
     public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
         dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
@@ -309,7 +369,7 @@
                         icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
                                 msg.arg2, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getTextAfterCursor", getIMM(), icProto);
+                                TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -339,7 +399,7 @@
                         icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
                                 msg.arg2, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getTextBeforeCursor", getIMM(), icProto);
+                                TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -368,7 +428,7 @@
                     if (ImeTracing.getInstance().isEnabled()) {
                         icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getSelectedText", getIMM(), icProto);
+                                TAG + "#getSelectedText", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -402,7 +462,7 @@
                         icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
                                 afterLength, flags, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getSurroundingText", getIMM(), icProto);
+                                TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -432,7 +492,7 @@
                         icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
                                 result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getCursorCapsMode", getIMM(), icProto);
+                                TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -464,7 +524,7 @@
                         icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
                                 msg.arg1, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getExtractedText", getIMM(), icProto);
+                                TAG + "#getExtractedText", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8d82e33..c336373 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,8 +35,7 @@
  * {@hide}
  */
 oneway interface IInputMethod {
-    void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
-             int configChanges);
+    void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
 
     void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
             in IInlineSuggestionsRequestCallback cb);
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index f29e95c..c9755a3 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -204,6 +204,8 @@
     @Nullable
     private final float[] mActivityViewToScreenMatrixValues;
 
+    public final boolean isInputMethodSuppressingSpellChecker;
+
     /**
      * @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}.
      *         {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}.
@@ -220,7 +222,8 @@
 
     public InputBindResult(@ResultCode int _result,
             IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
-            @Nullable Matrix activityViewToScreenMatrix) {
+            @Nullable Matrix activityViewToScreenMatrix,
+            boolean isInputMethodSuppressingSpellChecker) {
         result = _result;
         method = _method;
         channel = _channel;
@@ -232,6 +235,7 @@
             mActivityViewToScreenMatrixValues = new float[9];
             activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
         }
+        this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker;
     }
 
     InputBindResult(Parcel source) {
@@ -245,6 +249,7 @@
         id = source.readString();
         sequence = source.readInt();
         mActivityViewToScreenMatrixValues = source.createFloatArray();
+        isInputMethodSuppressingSpellChecker = source.readBoolean();
     }
 
     @Override
@@ -252,6 +257,7 @@
         return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
                 + " sequence=" + sequence
                 + " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
+                + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
                 + "}";
     }
 
@@ -274,6 +280,7 @@
         dest.writeString(id);
         dest.writeInt(sequence);
         dest.writeFloatArray(mActivityViewToScreenMatrixValues);
+        dest.writeBoolean(isInputMethodSuppressingSpellChecker);
     }
 
     /**
@@ -340,7 +347,7 @@
     }
 
     private static InputBindResult error(@ResultCode int result) {
-        return new InputBindResult(result, null, null, null, -1, null);
+        return new InputBindResult(result, null, null, null, -1, null, false);
     }
 
     /**
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
index 851d1f3..eb2478f 100644
--- a/core/java/com/android/internal/view/OWNERS
+++ b/core/java/com/android/internal/view/OWNERS
@@ -18,3 +18,7 @@
 per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index 6cc5a4a..83345da 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -100,7 +100,6 @@
         }
         // TODO(b/179178086): crop/clip the icon to a circle?
         mConversationIconView.setImageIcon(icon);
-        mConversationText.setText(callerName);
     }
 
     @RemotableViewMethod
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index d284d51..8e68be0 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -1,4 +1,6 @@
 per-file PointerLocationView.java = michaelwr@google.com, svv@google.com
+per-file RecyclerView.java = mount@google.com
+per-file ViewPager.java = mount@google.com
 
 # LockSettings related
 per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 89a90e9..17ce75e 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -16,10 +16,16 @@
 
 package com.android.internal.widget;
 
+import static android.widget.EdgeEffect.TYPE_GLOW;
+import static android.widget.EdgeEffect.TYPE_STRETCH;
+import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_BY_DEFAULT;
+import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED;
+
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.compat.Compatibility;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -460,6 +466,8 @@
     private final int[] mScrollConsumed = new int[2];
     private final int[] mNestedOffsets = new int[2];
 
+    private int mEdgeEffectType;
+
     /**
      * These are views that had their a11y importance changed during a layout. We defer these events
      * until the end of the layout because a11y service may make sync calls back to the RV while
@@ -587,6 +595,14 @@
             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
         }
 
+        boolean defaultToStretch = Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
+                || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED);
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.EdgeEffect);
+        mEdgeEffectType = a.getInt(com.android.internal.R.styleable.EdgeEffect_edgeEffectType,
+                defaultToStretch ? TYPE_STRETCH : TYPE_GLOW);
+        a.recycle();
+
         // Re-set whether nested scrolling is enabled so that it is set on all API levels
         setNestedScrollingEnabled(nestedScrollingEnabled);
     }
@@ -610,6 +626,28 @@
     }
 
     /**
+     * Returns the {@link EdgeEffect#getType()} used for all EdgeEffects.
+     *
+     * @return @link EdgeEffect#getType()} used for all EdgeEffects.
+     */
+    @EdgeEffect.EdgeEffectType
+    public int getEdgeEffectType() {
+        return mEdgeEffectType;
+    }
+
+    /**
+     * Sets the {@link EdgeEffect#getType()} used in all EdgeEffects.
+     * Any existing over-scroll effects are cleared and new effects are created as needed.
+     *
+     * @param type the {@link EdgeEffect#getType()} used in all EdgeEffects.
+     */
+    public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+        mEdgeEffectType = type;
+        invalidateGlows();
+        invalidate();
+    }
+
+    /**
      * Instantiate and set a LayoutManager, if specified in the attributes.
      */
     private void createLayoutManager(Context context, String className, AttributeSet attrs,
@@ -2183,6 +2221,7 @@
             return;
         }
         mLeftGlow = new EdgeEffect(getContext());
+        mLeftGlow.setType(mEdgeEffectType);
         if (mClipToPadding) {
             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2196,6 +2235,7 @@
             return;
         }
         mRightGlow = new EdgeEffect(getContext());
+        mRightGlow.setType(mEdgeEffectType);
         if (mClipToPadding) {
             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2209,6 +2249,7 @@
             return;
         }
         mTopGlow = new EdgeEffect(getContext());
+        mTopGlow.setType(mEdgeEffectType);
         if (mClipToPadding) {
             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2223,6 +2264,7 @@
             return;
         }
         mBottomGlow = new EdgeEffect(getContext());
+        mBottomGlow.setType(mEdgeEffectType);
         if (mClipToPadding) {
             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2663,7 +2705,7 @@
                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
 
-                if (mScrollState == SCROLL_STATE_SETTLING) {
+                if (stopGlowAnimations(e) || mScrollState == SCROLL_STATE_SETTLING) {
                     getParent().requestDisallowInterceptTouchEvent(true);
                     setScrollState(SCROLL_STATE_DRAGGING);
                 }
@@ -2731,6 +2773,38 @@
         return mScrollState == SCROLL_STATE_DRAGGING;
     }
 
+    /**
+     * This stops any edge glow animation that is currently running by applying a
+     * 0 length pull at the displacement given by the provided MotionEvent. On pre-S devices,
+     * this method does nothing, allowing any animating edge effect to continue animating and
+     * returning <code>false</code> always.
+     *
+     * @param e The motion event to use to indicate the finger position for the displacement of
+     *          the current pull.
+     * @return <code>true</code> if any edge effect had an existing effect to be drawn ond the
+     * animation was stopped or <code>false</code> if no edge effect had a value to display.
+     */
+    private boolean stopGlowAnimations(MotionEvent e) {
+        boolean stopped = false;
+        if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
+            mLeftGlow.onPullDistance(0, 1 - (e.getY() / getHeight()));
+            stopped = true;
+        }
+        if (mRightGlow != null && mRightGlow.getDistance() != 0) {
+            mRightGlow.onPullDistance(0, e.getY() / getHeight());
+            stopped = true;
+        }
+        if (mTopGlow != null && mTopGlow.getDistance() != 0) {
+            mTopGlow.onPullDistance(0, e.getX() / getWidth());
+            stopped = true;
+        }
+        if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
+            mBottomGlow.onPullDistance(0, 1 - e.getX() / getWidth());
+            stopped = true;
+        }
+        return stopped;
+    }
+
     @Override
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
         final int listenerCount = mOnItemTouchListeners.size();
@@ -2807,6 +2881,8 @@
                 final int y = (int) (e.getY(index) + 0.5f);
                 int dx = mLastTouchX - x;
                 int dy = mLastTouchY - y;
+                dx -= releaseHorizontalGlow(dx, e.getY());
+                dy -= releaseVerticalGlow(dy, e.getX());
 
                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
                     dx -= mScrollConsumed[0];
@@ -2887,6 +2963,72 @@
         return true;
     }
 
+    /**
+     * If either of the horizontal edge glows are currently active, this consumes part or all of
+     * deltaX on the edge glow.
+     *
+     * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive
+     *                         for moving down and negative for moving up.
+     * @param y The vertical position of the pointer.
+     * @return The amount of <code>deltaX</code> that has been consumed by the
+     * edge glow.
+     */
+    private int releaseHorizontalGlow(int deltaX, float y) {
+        // First allow releasing existing overscroll effect:
+        float consumed = 0;
+        float displacement = y / getHeight();
+        float pullDistance = (float) deltaX / getWidth();
+        if (mLeftGlow != null && mLeftGlow.getDistance() != 0) {
+            consumed = -mLeftGlow.onPullDistance(-pullDistance, 1 - displacement);
+            if (mLeftGlow.getDistance() == 0) {
+                mLeftGlow.onRelease();
+            }
+        } else if (mRightGlow != null && mRightGlow.getDistance() != 0) {
+            consumed = mRightGlow.onPullDistance(pullDistance, displacement);
+            if (mRightGlow.getDistance() == 0) {
+                mRightGlow.onRelease();
+            }
+        }
+        int pixelsConsumed = Math.round(consumed * getWidth());
+        if (pixelsConsumed != 0) {
+            invalidate();
+        }
+        return pixelsConsumed;
+    }
+
+    /**
+     * If either of the vertical edge glows are currently active, this consumes part or all of
+     * deltaY on the edge glow.
+     *
+     * @param deltaY The pointer motion, in pixels, in the vertical direction, positive
+     *                         for moving down and negative for moving up.
+     * @param x The vertical position of the pointer.
+     * @return The amount of <code>deltaY</code> that has been consumed by the
+     * edge glow.
+     */
+    private int releaseVerticalGlow(int deltaY, float x) {
+        // First allow releasing existing overscroll effect:
+        float consumed = 0;
+        float displacement = x / getWidth();
+        float pullDistance = (float) deltaY / getHeight();
+        if (mTopGlow != null && mTopGlow.getDistance() != 0) {
+            consumed = -mTopGlow.onPullDistance(-pullDistance, displacement);
+            if (mTopGlow.getDistance() == 0) {
+                mTopGlow.onRelease();
+            }
+        } else if (mBottomGlow != null && mBottomGlow.getDistance() != 0) {
+            consumed = mBottomGlow.onPullDistance(pullDistance, 1 - displacement);
+            if (mBottomGlow.getDistance() == 0) {
+                mBottomGlow.onRelease();
+            }
+        }
+        int pixelsConsumed = Math.round(consumed * getHeight());
+        if (pixelsConsumed != 0) {
+            invalidate();
+        }
+        return pixelsConsumed;
+    }
+
     private void resetTouch() {
         if (mVelocityTracker != null) {
             mVelocityTracker.clear();
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cea8b44..a153fab 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -1,4 +1,3 @@
-
 package {
     default_applicable_licenses: ["frameworks_base_core_jni_license"],
 }
@@ -69,10 +68,9 @@
         "liblog",
         "libminikin",
         "libz",
-        "libziparchive",
     ],
 
-    static_libs: ["libnativehelper_lazy"],
+    static_libs: ["libnativehelper_lazy", "libziparchive_for_incfs", ],
 
     export_include_dirs: [
         ".",
@@ -219,6 +217,7 @@
                 "fd_utils.cpp",
                 "android_hardware_input_InputWindowHandle.cpp",
                 "android_hardware_input_InputApplicationHandle.cpp",
+                "permission_utils.cpp",
             ],
 
             static_libs: [
@@ -237,6 +236,7 @@
                 "audioflinger-aidl-cpp",
                 "av-types-aidl-cpp",
                 "android.hardware.camera.device@3.2",
+                "media_permission-aidl-cpp",
                 "libandroidicu",
                 "libbpf_android",
                 "libnetdbpf",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7ad1b49..94ac183 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -97,6 +97,7 @@
 extern int register_android_media_MicrophoneInfo(JNIEnv *env);
 extern int register_android_media_ToneGenerator(JNIEnv *env);
 extern int register_android_media_midi(JNIEnv *env);
+extern int register_android_media_permission_Identity(JNIEnv* env);
 
 namespace android {
 
@@ -636,6 +637,12 @@
     char saveResolvedClassesDelayMsOptsBuf[
             sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
     char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeVdex[
+            sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeOdex[
+            sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeArt[
+            sizeof("-XMadviseWillNeedArtFileSize:")-1 + PROPERTY_VALUE_MAX];
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -844,6 +851,22 @@
     parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
 
     /*
+     * Use default platform configuration as limits for madvising,
+     * when no properties are specified.
+     */
+    parseRuntimeOption("dalvik.vm.madvise.vdexfile.size",
+                       madviseWillNeedFileSizeVdex,
+                       "-XMadviseWillNeedVdexFileSize:");
+
+    parseRuntimeOption("dalvik.vm.madvise.odexfile.size",
+                       madviseWillNeedFileSizeOdex,
+                       "-XMadviseWillNeedOdexFileSize:");
+
+    parseRuntimeOption("dalvik.vm.madvise.artfile.size",
+                       madviseWillNeedFileSizeArt,
+                       "-XMadviseWillNeedArtFileSize:");
+
+    /*
      * Profile related options.
      */
     parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
@@ -1564,6 +1587,7 @@
         REG_JNI(register_android_media_RemoteDisplay),
         REG_JNI(register_android_media_ToneGenerator),
         REG_JNI(register_android_media_midi),
+        REG_JNI(register_android_media_permission_Identity),
 
         REG_JNI(register_android_opengl_classes),
         REG_JNI(register_android_server_NetworkManagementSocketTagger),
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 0e0f98e..7d3febb 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -22,6 +22,7 @@
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
+#include "permission_utils.h"
 
 #include <utils/Log.h>
 #include <media/AudioRecord.h>
@@ -39,6 +40,9 @@
 
 // ----------------------------------------------------------------------------
 
+using android::media::permission::convertIdentity;
+using android::media::permission::Identity;
+
 using namespace android;
 
 // ----------------------------------------------------------------------------
@@ -181,12 +185,11 @@
 }
 
 // ----------------------------------------------------------------------------
-static jint
-android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-        jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
-        jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
-        jlong nativeRecordInJavaObj)
-{
+static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+                                            jobject jaa, jintArray jSampleRate, jint channelMask,
+                                            jint channelIndexMask, jint audioFormat,
+                                            jint buffSizeInBytes, jintArray jSession,
+                                            jobject jIdentity, jlong nativeRecordInJavaObj) {
     //ALOGV(">> Entering android_media_AudioRecord_setup");
     //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
     //     "nativeRecordInJavaObj=0x%llX",
@@ -262,10 +265,8 @@
         size_t frameSize = channelCount * bytesPerSample;
         size_t frameCount = buffSizeInBytes / frameSize;
 
-        ScopedUtfChars opPackageNameStr(env, opPackageName);
-
         // create an uninitialized AudioRecord object
-        lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
+        lpRecorder = new AudioRecord(convertIdentity(env, jIdentity));
 
         // read the AudioAttributes values
         auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -373,8 +374,6 @@
     return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
 }
 
-
-
 // ----------------------------------------------------------------------------
 static jint
 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
@@ -893,9 +892,11 @@
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
+        // name,               signature,  funcPtr
         {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
         {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
-        {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
+        {"native_setup",
+         "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/media/permission/Identity;J)I",
          (void *)android_media_AudioRecord_setup},
         {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
         {"native_release", "()V", (void *)android_media_AudioRecord_release},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 94bd28a..f102edc 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2204,13 +2204,11 @@
     return jStatus;
 }
 
-static jint
-android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
-                                             jobject jSurroundFormats, jboolean reported)
-{
+static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
+                                                         jobject jSurroundFormats) {
     ALOGV("getSurroundFormats");
 
-    if (jSurroundFormats == NULL) {
+    if (jSurroundFormats == nullptr) {
         ALOGE("jSurroundFormats is NULL");
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
@@ -2221,10 +2219,10 @@
 
     jint jStatus;
     unsigned int numSurroundFormats = 0;
-    audio_format_t *surroundFormats = NULL;
-    bool *surroundFormatsEnabled = NULL;
-    status_t status = AudioSystem::getSurroundFormats(
-            &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+    audio_format_t *surroundFormats = nullptr;
+    bool *surroundFormatsEnabled = nullptr;
+    status_t status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats,
+                                                      surroundFormatsEnabled);
     if (status != NO_ERROR) {
         ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
         jStatus = nativeToJavaStatus(status);
@@ -2236,8 +2234,8 @@
     }
     surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
     surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool));
-    status = AudioSystem::getSurroundFormats(
-            &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+    status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats,
+                                             surroundFormatsEnabled);
     jStatus = nativeToJavaStatus(status);
     if (status != NO_ERROR) {
         ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
@@ -2258,6 +2256,50 @@
     return jStatus;
 }
 
+static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jobject thiz,
+                                                                 jobject jSurroundFormats) {
+    ALOGV("getReportedSurroundFormats");
+
+    if (jSurroundFormats == nullptr) {
+        ALOGE("jSurroundFormats is NULL");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jSurroundFormats, gArrayListClass)) {
+        ALOGE("jSurroundFormats not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    jint jStatus;
+    unsigned int numSurroundFormats = 0;
+    audio_format_t *surroundFormats = nullptr;
+    status_t status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+    if (status != NO_ERROR) {
+        ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status);
+        jStatus = nativeToJavaStatus(status);
+        goto exit;
+    }
+    if (numSurroundFormats == 0) {
+        jStatus = (jint)AUDIO_JAVA_SUCCESS;
+        goto exit;
+    }
+    surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
+    status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+    jStatus = nativeToJavaStatus(status);
+    if (status != NO_ERROR) {
+        ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status);
+        goto exit;
+    }
+    for (size_t i = 0; i < numSurroundFormats; i++) {
+        jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor,
+                                                audioFormatFromNative(surroundFormats[i]));
+        env->CallObjectMethod(jSurroundFormats, gArrayListMethods.add, surroundFormat);
+        env->DeleteLocalRef(surroundFormat);
+    }
+
+exit:
+    free(surroundFormats);
+    return jStatus;
+}
+
 static jint
 android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz,
                                                    jint audioFormat, jboolean enabled)
@@ -2619,8 +2661,10 @@
           (void *)android_media_AudioSystem_getOffloadSupport},
          {"getMicrophones", "(Ljava/util/ArrayList;)I",
           (void *)android_media_AudioSystem_getMicrophones},
-         {"getSurroundFormats", "(Ljava/util/Map;Z)I",
+         {"getSurroundFormats", "(Ljava/util/Map;)I",
           (void *)android_media_AudioSystem_getSurroundFormats},
+         {"getReportedSurroundFormats", "(Ljava/util/ArrayList;)I",
+          (void *)android_media_AudioSystem_getReportedSurroundFormats},
          {"setSurroundFormatEnabled", "(IZ)I",
           (void *)android_media_AudioSystem_setSurroundFormatEnabled},
          {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index cae6db5..62767a6 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -48,6 +48,7 @@
 using namespace android;
 
 using ::android::media::VolumeShaper;
+using ::android::media::permission::Identity;
 
 // ----------------------------------------------------------------------------
 static const char* const kClassPathName = "android/media/AudioTrack";
@@ -328,7 +329,10 @@
 
         // create the native AudioTrack object
         ScopedUtfChars opPackageNameStr(env, opPackageName);
-        lpTrack = new AudioTrack(opPackageNameStr.c_str());
+        // TODO b/182469354: make consistent with AudioRecord
+        Identity identity = Identity();
+        identity.packageName = std::string(opPackageNameStr.c_str());
+        lpTrack = new AudioTrack(identity);
 
         // read the AudioAttributes values
         auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -390,8 +394,8 @@
                                   sessionId, // audio session ID
                                   offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
                                           : AudioTrack::TRANSFER_SYNC,
-                                  (offload || encapsulationMode) ? &offloadInfo : NULL, -1,
-                                  -1, // default uid, pid values
+                                  (offload || encapsulationMode) ? &offloadInfo : NULL,
+                                  Identity(), // default uid, pid values
                                   paa.get());
             break;
 
@@ -416,8 +420,8 @@
                                   true,                   // thread can call Java
                                   sessionId,              // audio session ID
                                   AudioTrack::TRANSFER_SHARED,
-                                  NULL,   // default offloadInfo
-                                  -1, -1, // default uid, pid values
+                                  NULL,       // default offloadInfo
+                                  Identity(), // default uid, pid values
                                   paa.get());
             break;
 
diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp
index 7508108..a781a37 100644
--- a/core/jni/android_net_NetworkUtils.cpp
+++ b/core/jni/android_net_NetworkUtils.cpp
@@ -52,27 +52,6 @@
 // FrameworkListener limits the size of commands to 4096 bytes.
 constexpr int MAXCMDSIZE = 4096;
 
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-    static jclass errnoExceptionClass =
-            MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
-    static jmethodID errnoExceptionCtor =
-            GetMethodIDOrDie(env, errnoExceptionClass,
-            "<init>", "(Ljava/lang/String;I)V");
-
-    jobject exception = env->NewObject(errnoExceptionClass,
-                                       errnoExceptionCtor,
-                                       detailMessage.get(),
-                                       error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
 static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
     struct sock_filter filter_code[] = {
@@ -150,7 +129,7 @@
     int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
 
     if (fd < 0) {
-        throwErrnoException(env, "resNetworkQuery", -fd);
+        jniThrowErrnoException(env, "resNetworkQuery", -fd);
         return nullptr;
     }
 
@@ -165,7 +144,7 @@
     int fd = resNetworkSend(netId, data, msgLen, flags);
 
     if (fd < 0) {
-        throwErrnoException(env, "resNetworkSend", -fd);
+        jniThrowErrnoException(env, "resNetworkSend", -fd);
         return nullptr;
     }
 
@@ -180,13 +159,13 @@
     int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
     jniSetFileDescriptorOfFD(env, javaFd, -1);
     if (res < 0) {
-        throwErrnoException(env, "resNetworkResult", -res);
+        jniThrowErrnoException(env, "resNetworkResult", -res);
         return nullptr;
     }
 
     jbyteArray answer = env->NewByteArray(res);
     if (answer == nullptr) {
-        throwErrnoException(env, "resNetworkResult", ENOMEM);
+        jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
         return nullptr;
     } else {
         env->SetByteArrayRegion(answer, 0, res,
@@ -208,7 +187,7 @@
 static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
     unsigned dnsNetId = 0;
     if (int res = getNetworkForDns(&dnsNetId) < 0) {
-        throwErrnoException(env, "getDnsNetId", -res);
+        jniThrowErrnoException(env, "getDnsNetId", -res);
         return nullptr;
     }
     bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
@@ -233,8 +212,8 @@
     // Obtain the parameters of the TCP repair window.
     int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
     if (rc == -1) {
-      throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
-      return NULL;
+        jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+        return NULL;
     }
 
     struct tcp_info tcpinfo = {};
@@ -244,8 +223,8 @@
     // should be applied to the window size.
     rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
     if (rc == -1) {
-      throwErrnoException(env, "getsockopt : TCP_INFO", errno);
-      return NULL;
+        jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
+        return NULL;
     }
 
     jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index dc86187..fe375f4 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -35,21 +35,6 @@
 jclass errnoExceptionClass;
 jmethodID errnoExceptionCtor;  // MethodID for ErrnoException.<init>(String,I)
 
-void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-
-    jobject exception = env->NewObject(errnoExceptionClass,
-                                       errnoExceptionCtor,
-                                       detailMessage.get(),
-                                       error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
 jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
 
     // Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
@@ -65,7 +50,7 @@
     }
 
     if (fd < 0) {
-        throwErrnoException(env, "SharedMemory_create", err);
+        jniThrowErrnoException(env, "SharedMemory_create", err);
         return nullptr;
     }
 
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 2384efa..413bcef 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -41,6 +41,10 @@
     return (jboolean)IncFs_IsIncFsPath(path.c_str());
 }
 
+static jboolean nativeIsIncrementalFd(JNIEnv* env, jobject clazz, jint fd) {
+    return (jboolean)IncFs_IsIncFsFd(fd);
+}
+
 static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) {
     ScopedUtfChars path(env, javaPath);
 
@@ -61,6 +65,7 @@
         {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
          {"nativeIsV2Available", "()Z", (void*)nativeIsV2Available},
          {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
+         {"nativeIsIncrementalFd", "(I)Z", (void*)nativeIsIncrementalFd},
          {"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B",
           (void*)nativeUnsafeGetFileSignature}};
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f7b3f30..2e4be14 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1509,7 +1509,9 @@
             res = JNI_TRUE;
         } else {
             jniThrowException(env, "java/util/NoSuchElementException",
-                              "Death link does not exist");
+                              base::StringPrintf("Death link does not exist (%s)",
+                                                 statusToString(err).c_str())
+                                      .c_str());
         }
     }
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index dcfa950..d4b5c2b 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1316,24 +1316,6 @@
     return removeAllProcessGroups();
 }
 
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-    static jclass errnoExceptionClass =
-            MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
-    static jmethodID errnoExceptionCtor =
-            GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V");
-
-    jobject exception =
-            env->NewObject(errnoExceptionClass, errnoExceptionCtor, detailMessage.get(), error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
 // Wrapper function to the syscall pidfd_open, which creates a file
 // descriptor that refers to the process whose PID is specified in pid.
 static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
@@ -1343,7 +1325,7 @@
 static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
     int fd = sys_pidfd_open(pid, flags);
     if (fd < 0) {
-        throwErrnoException(env, "nativePidFdOpen", errno);
+        jniThrowErrnoException(env, "nativePidFdOpen", errno);
         return -1;
     }
     return fd;
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 9746a07..97fdb43 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -34,6 +34,8 @@
 
 #include "core_jni_helpers.h"
 
+using android::base::Result;
+
 namespace android {
 
 // Log debug messages about the dispatch cycle.
@@ -197,16 +199,9 @@
     ScopedLocalRef<jobject> senderObj(env, NULL);
     bool skipCallbacks = false;
     for (;;) {
-        uint32_t publishedSeq;
-        bool handled;
-        std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
-                [&publishedSeq, &handled](uint32_t inSeq, bool inHandled,
-                                          nsecs_t inConsumeTime) -> void {
-            publishedSeq = inSeq;
-            handled = inHandled;
-        };
-        status_t status = mInputPublisher.receiveFinishedSignal(callback);
-        if (status) {
+        Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal();
+        if (!result.ok()) {
+            const status_t status = result.error().code();
             if (status == WOULD_BLOCK) {
                 return OK;
             }
@@ -215,7 +210,7 @@
             return status;
         }
 
-        auto it = mPublishedSeqMap.find(publishedSeq);
+        auto it = mPublishedSeqMap.find(result->seq);
         if (it == mPublishedSeqMap.end()) {
             continue;
         }
@@ -225,9 +220,9 @@
 
         if (kDebugDispatchCycle) {
             ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
-                    "pendingEvents=%zu.",
-                    getInputChannelName().c_str(), seq, handled ? "true" : "false",
-                    mPublishedSeqMap.size());
+                  "pendingEvents=%zu.",
+                  getInputChannelName().c_str(), seq, result->handled ? "true" : "false",
+                  mPublishedSeqMap.size());
         }
 
         if (!skipCallbacks) {
@@ -241,8 +236,8 @@
             }
 
             env->CallVoidMethod(senderObj.get(),
-                    gInputEventSenderClassInfo.dispatchInputEventFinished,
-                    jint(seq), jboolean(handled));
+                                gInputEventSenderClassInfo.dispatchInputEventFinished,
+                                static_cast<jint>(seq), static_cast<jboolean>(result->handled));
             if (env->ExceptionCheck()) {
                 ALOGE("Exception dispatching finished signal.");
                 skipCallbacks = true;
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ebc507a..469e577 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -16,9 +16,10 @@
 
 #include <android_runtime/AndroidRuntime.h>
 
-#include <input/KeyCharacterMap.h>
-#include <input/Input.h>
 #include <binder/Parcel.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/KeyCharacterMap.h>
 
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
@@ -75,6 +76,10 @@
                           reinterpret_cast<jlong>(nativeMap));
 }
 
+static jobject nativeObtainEmptyKeyCharacterMap(JNIEnv* env, jobject /* clazz */, jint deviceId) {
+    return android_view_KeyCharacterMap_create(env, deviceId, nullptr);
+}
+
 static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     if (!parcel) {
@@ -224,33 +229,37 @@
     return result;
 }
 
+static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) {
+    const std::shared_ptr<KeyCharacterMap>& map1 =
+            (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap();
+    const std::shared_ptr<KeyCharacterMap>& map2 =
+            (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap();
+    if (map1 == nullptr || map2 == nullptr) {
+        return map1 == map2;
+    }
+    return static_cast<jboolean>(*map1 == *map2);
+}
 
 /*
  * JNI registration.
  */
 
 static const JNINativeMethod g_methods[] = {
-    /* name, signature, funcPtr */
-    { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
-            (void*)nativeReadFromParcel },
-    { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
-            (void*)nativeWriteToParcel },
-    { "nativeDispose", "(J)V",
-            (void*)nativeDispose },
-    { "nativeGetCharacter", "(JII)C",
-            (void*)nativeGetCharacter },
-    { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
-            (void*)nativeGetFallbackAction },
-    { "nativeGetNumber", "(JI)C",
-            (void*)nativeGetNumber },
-    { "nativeGetMatch", "(JI[CI)C",
-            (void*)nativeGetMatch },
-    { "nativeGetDisplayLabel", "(JI)C",
-            (void*)nativeGetDisplayLabel },
-    { "nativeGetKeyboardType", "(J)I",
-            (void*)nativeGetKeyboardType },
-    { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;",
-            (void*)nativeGetEvents },
+        /* name, signature, funcPtr */
+        {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel},
+        {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
+        {"nativeDispose", "(J)V", (void*)nativeDispose},
+        {"nativeGetCharacter", "(JII)C", (void*)nativeGetCharacter},
+        {"nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
+         (void*)nativeGetFallbackAction},
+        {"nativeGetNumber", "(JI)C", (void*)nativeGetNumber},
+        {"nativeGetMatch", "(JI[CI)C", (void*)nativeGetMatch},
+        {"nativeGetDisplayLabel", "(JI)C", (void*)nativeGetDisplayLabel},
+        {"nativeGetKeyboardType", "(J)I", (void*)nativeGetKeyboardType},
+        {"nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", (void*)nativeGetEvents},
+        {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
+         (void*)nativeObtainEmptyKeyCharacterMap},
+        {"nativeEquals", "(JJ)Z", (void*)nativeEquals},
 };
 
 int register_android_view_KeyCharacterMap(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
index 6b41b2e..d644e37 100644
--- a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
@@ -30,13 +30,13 @@
 
 static jlongArray KernelCpuBpfTracking_getFreqsInternal(JNIEnv *env, jobject) {
     auto freqs = android::bpf::getCpuFreqs();
-    if (!freqs) return NULL;
+    if (!freqs) return nullptr;
 
     std::vector<uint64_t> allFreqs;
     for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
 
     auto ar = env->NewLongArray(allFreqs.size());
-    if (ar != NULL) {
+    if (ar != nullptr) {
         env->SetLongArrayRegion(ar, 0, allFreqs.size(),
                                 reinterpret_cast<const jlong *>(allFreqs.data()));
     }
@@ -45,7 +45,7 @@
 
 static jintArray KernelCpuBpfTracking_getFreqsClustersInternal(JNIEnv *env, jobject) {
     auto freqs = android::bpf::getCpuFreqs();
-    if (!freqs) return NULL;
+    if (!freqs) return nullptr;
 
     std::vector<uint32_t> freqsClusters;
     uint32_t clusters = freqs->size();
@@ -54,7 +54,7 @@
     }
 
     auto ar = env->NewIntArray(freqsClusters.size());
-    if (ar != NULL) {
+    if (ar != nullptr) {
         env->SetIntArrayRegion(ar, 0, freqsClusters.size(),
                                reinterpret_cast<const jint *>(freqsClusters.data()));
     }
diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
index ad43014..472bd23 100644
--- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
@@ -22,7 +22,7 @@
 
 static jlongArray KernelCpuTotalBpfMapReader_readInternal(JNIEnv *env, jobject) {
     auto freqTimes = android::bpf::getTotalCpuFreqTimes();
-    if (!freqTimes) return JNI_FALSE;
+    if (!freqTimes) return nullptr;
 
     std::vector<uint64_t> allTimes;
     for (const auto &vec : *freqTimes) {
@@ -32,7 +32,7 @@
     }
 
     auto ar = env->NewLongArray(allTimes.size());
-    if (ar != NULL) {
+    if (ar != nullptr) {
         env->SetLongArrayRegion(ar, 0, allTimes.size(),
                                 reinterpret_cast<const jlong *>(allTimes.data()));
     }
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
index 7900d30..098a4d8 100644
--- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -37,7 +37,7 @@
     jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
     if (!ar) {
         ar = env->NewLongArray(sz);
-        if (ar == NULL) return ar;
+        if (ar == nullptr) return ar;
         env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
     }
     return ar;
@@ -65,7 +65,7 @@
     static uint64_t lastUpdate = 0;
     uint64_t newLastUpdate = lastUpdate;
     auto sparseAr = env->GetObjectField(thiz, gmData);
-    if (sparseAr == NULL) return false;
+    if (sparseAr == nullptr) return false;
     auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
     if (!data.has_value()) return false;
 
@@ -75,7 +75,7 @@
             for (const auto &subVec : times) s += subVec.size();
         }
         jlongArray ar = getUidArray(env, sparseAr, uid, s);
-        if (ar == NULL) return false;
+        if (ar == nullptr) return false;
         copy2DVecToArray(env, ar, times);
     }
     lastUpdate = newLastUpdate;
@@ -91,7 +91,7 @@
     static uint64_t lastUpdate = 0;
     uint64_t newLastUpdate = lastUpdate;
     auto sparseAr = env->GetObjectField(thiz, gmData);
-    if (sparseAr == NULL) return false;
+    if (sparseAr == nullptr) return false;
     auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
     if (!data.has_value()) return false;
 
@@ -99,7 +99,7 @@
         // TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
         for (auto &time : times.active) time /= NSEC_PER_MSEC;
         jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
-        if (ar == NULL) return false;
+        if (ar == nullptr) return false;
         env->SetLongArrayRegion(ar, 0, times.active.size(),
                                 reinterpret_cast<const jlong *>(times.active.data()));
     }
@@ -111,7 +111,7 @@
     jlong nCpus = get_nprocs_conf();
 
     auto ar = env->NewLongArray(1);
-    if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
+    if (ar != nullptr) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
     return ar;
 }
 
@@ -124,7 +124,7 @@
     static uint64_t lastUpdate = 0;
     uint64_t newLastUpdate = lastUpdate;
     auto sparseAr = env->GetObjectField(thiz, gmData);
-    if (sparseAr == NULL) return false;
+    if (sparseAr == nullptr) return false;
     auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
     if (!data.has_value()) return false;
 
@@ -134,7 +134,7 @@
             for (const auto &subVec : times.policy) s += subVec.size();
         }
         jlongArray ar = getUidArray(env, sparseAr, uid, s);
-        if (ar == NULL) return false;
+        if (ar == nullptr) return false;
         copy2DVecToArray(env, ar, times.policy);
     }
     lastUpdate = newLastUpdate;
@@ -143,12 +143,12 @@
 
 static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
     auto times = android::bpf::getUidConcurrentTimes(0);
-    if (!times.has_value()) return NULL;
+    if (!times.has_value()) return nullptr;
 
     std::vector<jlong> clusterCores;
     for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
     auto ar = env->NewLongArray(clusterCores.size());
-    if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
+    if (ar != nullptr) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
     return ar;
 }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 836074f..be17d92 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -184,6 +184,17 @@
 /** The numeric value for the normal priority a process should have. */
 static constexpr int PROCESS_PRIORITY_DEFAULT = 0;
 
+/** Exponential back off parameters for storage dir check. */
+static constexpr unsigned int STORAGE_DIR_CHECK_RETRY_MULTIPLIER = 2;
+static constexpr unsigned int STORAGE_DIR_CHECK_INIT_INTERVAL_US = 50;
+static constexpr unsigned int STORAGE_DIR_CHECK_MAX_INTERVAL_US = 1000;
+/**
+ * Lower bound time we allow storage dir check to sleep.
+ * If it exceeds 2s, PROC_START_TIMEOUT_MSG will kill the starting app anyway,
+ * so it's fine to assume max retries is 5 mins.
+ */
+static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5;
+
 /**
  * A helper class containing accounting information for USAPs.
  */
@@ -814,7 +825,7 @@
   PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
              multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
 
-  bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
+  bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, true);
 
   if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
       const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
@@ -1458,6 +1469,31 @@
   }
 }
 
+static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn) {
+  unsigned int sleepIntervalUs = STORAGE_DIR_CHECK_INIT_INTERVAL_US;
+
+  // This is just an approximate value as it doesn't need to be very accurate.
+  unsigned int sleepTotalUs = 0;
+
+  const char* dir_path = target.c_str();
+  while (sleepTotalUs < STORAGE_DIR_CHECK_TIMEOUT_US) {
+    if (access(dir_path, F_OK) == 0) {
+      return;
+    }
+    // Failed, so we add exponential backoff and retry
+    usleep(sleepIntervalUs);
+    sleepTotalUs += sleepIntervalUs;
+    sleepIntervalUs = std::min<unsigned int>(
+        sleepIntervalUs * STORAGE_DIR_CHECK_RETRY_MULTIPLIER,
+        STORAGE_DIR_CHECK_MAX_INTERVAL_US);
+  }
+  // Last chance and get the latest errno if it fails.
+  if (access(dir_path, F_OK) == 0) {
+    return;
+  }
+  fail_fn(CREATE_ERROR("Error dir is not ready %s: %s", dir_path, strerror(errno)));
+}
+
 static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid,
     const char* dir_name, const char* package, fail_fn_t fail_fn) {
     bool hasSdcardFs = IsSdcardfsUsed();
@@ -1468,6 +1504,10 @@
         source = StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", user_id, user_id, dir_name,
                               package);
     }
+
+  // Directory might be not ready, as prepareStorageDirs() is running asynchronously in ProcessList,
+  // so wait until dir is created.
+  WaitUntilDirReady(source, fail_fn);
   std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package);
 
   // As the parent is mounted as tmpfs, we need to create the target dir here.
diff --git a/core/jni/permission_utils.cpp b/core/jni/permission_utils.cpp
new file mode 100644
index 0000000..2b7ef99
--- /dev/null
+++ b/core/jni/permission_utils.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include "permission_utils.h"
+#include "core_jni_helpers.h"
+
+static struct {
+    jfieldID fieldUid;            // Identity.uid
+    jfieldID fieldPid;            // Identity.pid
+    jfieldID fieldPackageName;    // Identity.packageName
+    jfieldID fieldAttributionTag; // Identity.attributionTag
+} javaIdentityFields;
+
+static const JNINativeMethod method_table[] = {
+        // no static methods, currently
+};
+
+int register_android_media_permission_Identity(JNIEnv* env) {
+    jclass identityClass = android::FindClassOrDie(env, "android/media/permission/Identity");
+    javaIdentityFields.fieldUid = android::GetFieldIDOrDie(env, identityClass, "uid", "I");
+    javaIdentityFields.fieldPid = android::GetFieldIDOrDie(env, identityClass, "pid", "I");
+    javaIdentityFields.fieldPackageName =
+            android::GetFieldIDOrDie(env, identityClass, "packageName", "Ljava/lang/String;");
+    javaIdentityFields.fieldAttributionTag =
+            android::GetFieldIDOrDie(env, identityClass, "attributionTag", "Ljava/lang/String;");
+
+    return android::RegisterMethodsOrDie(env, "android/media/permission/Identity", method_table,
+                                         NELEM(method_table));
+}
+
+namespace android::media::permission {
+
+Identity convertIdentity(JNIEnv* env, const jobject& jIdentity) {
+    Identity identity;
+
+    identity.uid = env->GetIntField(jIdentity, javaIdentityFields.fieldUid);
+    identity.pid = env->GetIntField(jIdentity, javaIdentityFields.fieldPid);
+
+    jstring packageNameStr = static_cast<jstring>(
+            env->GetObjectField(jIdentity, javaIdentityFields.fieldPackageName));
+    if (packageNameStr == nullptr) {
+        identity.packageName = std::nullopt;
+    } else {
+        identity.packageName = std::string(ScopedUtfChars(env, packageNameStr).c_str());
+    }
+
+    jstring attributionTagStr = static_cast<jstring>(
+            env->GetObjectField(jIdentity, javaIdentityFields.fieldAttributionTag));
+    if (attributionTagStr == nullptr) {
+        identity.attributionTag = std::nullopt;
+    } else {
+        identity.attributionTag = std::string(ScopedUtfChars(env, attributionTagStr).c_str());
+    }
+
+    return identity;
+}
+
+} // namespace android::media::permission
diff --git a/core/jni/permission_utils.h b/core/jni/permission_utils.h
new file mode 100644
index 0000000..d625bb6
--- /dev/null
+++ b/core/jni/permission_utils.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/media/permission/Identity.h>
+#include <jni.h>
+
+namespace android::media::permission {
+
+Identity convertIdentity(JNIEnv* env, const jobject& jIdentity);
+}
+
+int register_android_media_permission_Identity(JNIEnv* env);
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index e62b5c1..ea5e7f72 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,9 +14,10 @@
 # Frameworks
 ogunwale@google.com
 jjaggi@google.com
+kwekua@google.com
 roosa@google.com
 per-file package_item_info.proto = toddke@google.com
-per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
 per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 
 # Biometrics
diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS
index 296abd1..cc479e6 100644
--- a/core/proto/android/app/OWNERS
+++ b/core/proto/android/app/OWNERS
@@ -1 +1 @@
-per-file location_time_zone_manager.proto = nfuller@google.com, mingaleev@google.com
+per-file location_time_zone_manager.proto, time_zone_detector.proto = nfuller@google.com, mingaleev@google.com
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
index f44d549..891e9fc 100644
--- a/core/proto/android/app/location_time_zone_manager.proto
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -17,6 +17,7 @@
 syntax = "proto2";
 package android.app.time;
 
+import "frameworks/base/core/proto/android/app/time_zone_detector.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
 
 option java_multiple_files = true;
@@ -31,15 +32,6 @@
   repeated TimeZoneProviderStateProto secondary_provider_states = 3;
 }
 
-// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
-// detector.
-message GeolocationTimeZoneSuggestionProto {
-  option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-  repeated string zone_ids = 1;
-  repeated string debug_info = 2;
-}
-
 // The state tracked for a LocationTimeZoneProvider.
 message TimeZoneProviderStateProto {
   option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/app/time_zone_detector.proto b/core/proto/android/app/time_zone_detector.proto
new file mode 100644
index 0000000..b33ca1d
--- /dev/null
+++ b/core/proto/android/app/time_zone_detector.proto
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.app.time;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "TimeZoneDetectorProto";
+
+// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
+// detector.
+message GeolocationTimeZoneSuggestionProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  repeated string zone_ids = 1;
+  repeated string debug_info = 2;
+}
+
+/*
+ * An obfuscated and simplified time zone suggestion for metrics use.
+ *
+ * The suggestion's time zone IDs (which relate to location) are obfuscated by
+ * mapping them to an ordinal. When the ordinal is assigned consistently across
+ * several objects (i.e. so the same time zone ID is always mapped to the same
+ * ordinal), this allows comparisons between those objects. For example, we can
+ * answer "did these two suggestions agree?", "does the suggestion match the
+ * device's current time zone?", without leaking knowledge of location. Ordinals
+ * are also significantly more compact than full IANA TZDB IDs, albeit highly
+ * unstable and of limited use.
+ */
+message MetricsTimeZoneSuggestion {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  enum Type {
+    CERTAIN = 1;
+    UNCERTAIN = 2;
+  }
+  optional Type type = 1;
+
+  // The ordinals for time zone(s) in the suggestion. Always empty for
+  // UNCERTAIN, and can be empty for CERTAIN, for example when the device is in
+  // a disputed area / on an ocean.
+  repeated uint32 time_zone_ordinals = 2;
+}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 632d372..dca6002 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -192,6 +192,12 @@
     optional Camera camera = 12;
 
     optional SettingProto carrier_apps_handled = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Clipboard {
+        optional SettingProto show_access_notifications = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Clipboard clipboard = 89;
+
     optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
     repeated SettingProto completed_categories = 15;
     optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -647,5 +653,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 89;
+    // Next tag = 90;
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index f26bf7c..a7127ad 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -346,6 +346,7 @@
     optional int32 proc_id = 29;
     optional bool translucent = 30;
     optional bool pip_auto_enter_enabled = 31;
+    optional bool in_size_compat_mode = 32;
 }
 
 /* represents WindowToken */
@@ -406,7 +407,7 @@
     optional int64 finished_seamless_rotation_frame = 40;
     optional WindowFramesProto window_frames = 41;
     optional bool force_seamless_rotation = 42;
-    optional bool in_size_compat_mode = 43;
+    optional bool has_compat_scale = 43;
     optional float global_scale = 44;
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 991f5de..d783b44 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2775,11 +2775,11 @@
          The app can check whether it has this authorization by calling
          {@link android.provider.Settings#canDrawOverlays
          Settings.canDrawOverlays()}.
-         <p>Protection level: signature|appop|installer|appPredictor|pre23|development -->
+         <p>Protection level: signature|setup|appop|installer|appPredictor|pre23|development -->
     <permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
         android:label="@string/permlab_systemAlertWindow"
         android:description="@string/permdesc_systemAlertWindow"
-        android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" />
+        android:protectionLevel="signature|setup|appop|installer|appPredictor|pre23|development" />
 
     <!-- @SystemApi @hide Allows an application to create windows using the type
          {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -5259,15 +5259,20 @@
     <permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to manage speech recognition service.
+     @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_SPEECH_RECOGNITION"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to manage the content suggestions service.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
-         android:protectionLevel="signature" />
+        android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows an application to manage the app predictions service.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.MANAGE_APP_PREDICTIONS"
-         android:protectionLevel="signature|appPredictor" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows an application to manage the search ui service.
      @hide  <p>Not for use by third-party applications.</p> -->
@@ -5414,6 +5419,8 @@
          intents}.
          <p>Protection level: normal -->
     <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+                android:label="@string/permlab_fullScreenIntent"
+                android:description="@string/permdesc_fullScreenIntent"
                 android:protectionLevel="normal" />
 
     <!-- @SystemApi Allows requesting the framework broadcast the
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
index f92e6d6..b969fa4 100644
--- a/core/res/res/layout/notification_expand_button.xml
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -39,7 +39,7 @@
             android:layout_height="@dimen/notification_expand_button_pill_height"
             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
             android:gravity="center_vertical"
-            android:paddingLeft="8dp"
+            android:paddingStart="8dp"
             />
 
         <ImageView
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 2d1c342..b9a3625 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -27,7 +27,7 @@
         android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_action_list_height"
+        android:layout_marginBottom="@dimen/notification_content_margin"
         android:orientation="vertical"
         >
 
diff --git a/core/res/res/layout/notification_template_material_big_call.xml b/core/res/res/layout/notification_template_material_big_call.xml
new file mode 100644
index 0000000..1d50467
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_call.xml
@@ -0,0 +1,107 @@
+<?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.internal.widget.CallLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clipChildren="false"
+    android:tag="call"
+    android:theme="@style/Theme.DeviceDefault.Notification"
+    >
+
+    <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+    <include layout="@layout/notification_template_conversation_icon_container" />
+
+    <LinearLayout
+        android:id="@+id/notification_action_list_margin_target"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/notification_content_margin"
+        android:orientation="vertical"
+        >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="horizontal"
+            >
+
+            <LinearLayout
+                android:id="@+id/notification_main_column"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_marginStart="@dimen/conversation_content_start"
+                android:orientation="vertical"
+                android:minHeight="68dp"
+                >
+
+                <include
+                    layout="@layout/notification_template_conversation_header"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    />
+
+                <include layout="@layout/notification_template_text_multiline" />
+
+                <include
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/notification_progress_bar_height"
+                    android:layout_marginTop="@dimen/notification_progress_margin_top"
+                    layout="@layout/notification_template_progress"
+                    />
+            </LinearLayout>
+
+            <FrameLayout
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:minWidth="@dimen/notification_content_margin_end"
+                >
+
+                <include
+                    layout="@layout/notification_expand_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    />
+
+            </FrameLayout>
+
+        </LinearLayout>
+
+        <ViewStub
+            android:layout="@layout/notification_material_reply_text"
+            android:id="@+id/notification_material_reply_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            />
+
+        <include
+            layout="@layout/notification_template_smart_reply_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/notification_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginTop="@dimen/notification_content_margin"
+            />
+
+        <include layout="@layout/notification_material_action_list" />
+
+    </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 2954ba2..86e7dec 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -31,7 +31,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="top"
         android:layout_marginTop="@dimen/notification_content_margin_top"
-        android:layout_marginBottom="@dimen/notification_action_list_height"
+        android:layout_marginBottom="@dimen/notification_content_margin"
         android:clipToPadding="false"
         android:orientation="vertical"
         >
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
index 7b52ec3..5d9e761 100644
--- a/core/res/res/layout/notification_template_material_call.xml
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -19,7 +18,6 @@
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
     android:clipChildren="false"
     android:tag="call"
     android:theme="@style/Theme.DeviceDefault.Notification"
@@ -29,58 +27,46 @@
     <include layout="@layout/notification_template_conversation_icon_container" />
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
+        xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_action_list_height"
-        android:orientation="vertical"
+        android:layout_height="80dp"
+        android:orientation="horizontal"
         >
 
         <LinearLayout
+            android:id="@+id/notification_main_column"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:layout_gravity="top"
-            android:orientation="horizontal"
+            android:layout_marginStart="@dimen/conversation_content_start"
+            android:orientation="vertical"
+            android:minHeight="68dp"
             >
 
-            <LinearLayout
-                android:id="@+id/notification_main_column"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:layout_marginStart="@dimen/conversation_content_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
-                android:orientation="vertical"
-                android:minHeight="68dp"
-                >
-
-                <include
-                    layout="@layout/notification_template_conversation_header"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    />
-
-                <include layout="@layout/notification_template_text" />
-
-                <include
-                    android:layout_width="match_parent"
-                    android:layout_height="@dimen/notification_progress_bar_height"
-                    android:layout_marginTop="@dimen/notification_progress_margin_top"
-                    layout="@layout/notification_template_progress"
-                    />
-            </LinearLayout>
-
-            <!-- TODO(b/179178086): remove padding from main column when this is visible -->
-            <include layout="@layout/notification_expand_button"
+            <include
+                layout="@layout/notification_template_conversation_header"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="top|end"
                 />
 
+            <include layout="@layout/notification_template_text" />
+
         </LinearLayout>
 
-        <include layout="@layout/notification_material_action_list" />
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:minWidth="@dimen/notification_content_margin_end"
+            >
+
+            <include
+                layout="@layout/notification_expand_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                />
+
+        </FrameLayout>
 
     </LinearLayout>
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7c446a9..62278d5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3564,16 +3564,7 @@
         <attr name="__removed2" format="boolean" />
         <!-- Specifies whether the IME supports showing inline suggestions. -->
         <attr name="supportsInlineSuggestions" format="boolean" />
-        <!-- Specify one or more configuration changes that the IME will handle itself. If not
-             specified, the IME will be restarted if any of these configuration changes happen in
-              the system.  Otherwise, the IME will remain running and its
-             {@link android.inputmethodservice.InputMethodService#onConfigurationChanged}
-             method is called with the new configuration.
-             <p>Note that all of these configuration changes can impact the
-             resource values seen by the application, so you will generally need
-             to re-retrieve all resources (including view layouts, drawables, etc)
-             to correctly handle any configuration change.-->
-        <attr name="configChanges" />
+        <attr name="suppressesSpellChecker" format="boolean" />
     </declare-styleable>
 
     <!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
@@ -8173,7 +8164,7 @@
             <flag name="hide_from_picker" value="0x2" />
               <!-- The widget provides a default configuration. The host may decide not to launch
                    the provided configuration activity. -->
-           <flag name="configuration_optional" value="0x3" />
+           <flag name="configuration_optional" value="0x4" />
         </attr>
         <!-- A resource identifier for a string containing a short description of the widget. -->
         <attr name="description" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cc52655ad7..601d66e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -829,7 +829,6 @@
             {@code FLAG_ACTIVITY_MULTIPLE_TASK} is set.-->
         <enum name="singleInstancePerTask" value="4" />
     </attr>
-
     <!-- Specify the orientation an activity should be run in.  If not
          specified, it will run in the current preferred orientation
          of the screen.
@@ -1603,6 +1602,12 @@
        <enum name="sync" value="2" />
     </attr>
 
+    <!-- Attribution tag to be used for permission sub-attribution if a
+      permission is checked in  {@link android.content.Context#sendBroadcast(Intent, String)}.
+      Multiple tags can be specified separated by '|'.
+    -->
+    <attr name="attributionTags" format="string" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -2825,6 +2830,10 @@
 
              <p> See {@link android.content.pm.ActivityInfo#FLAG_PREFER_MINIMAL_POST_PROCESSING} -->
         <attr name="preferMinimalPostProcessing" format="boolean"/>
+        <!-- Specify the attributionTags to be used if a permission is required due to
+            {@link android.content.Context#sendBroadcast(Intent, String)} being used.
+            Multiple tags can be specified separated by '|'. -->
+        <attr name="attributionTags"/>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8bc3a52..f6fee88 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -324,9 +324,6 @@
         <item>"0,1"</item>
     </string-array>
 
-    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
-    <integer name="config_networkTransitionTimeout">60000</integer>
-
     <!-- Whether/how to notify the user on network switches. See LingerMonitor.java. -->
     <integer translatable="false" name="config_networkNotifySwitchType">0</integer>
 
@@ -339,16 +336,6 @@
          Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
     <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
 
-    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
-         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
-         and if that value is empty, the framework will use a hard-coded default.
-         This is *NOT* a URL that will always be used by the system network validation to detect
-         captive portals: NetworkMonitor may use different strategies and will not necessarily use
-         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
-         instead. -->
-    <!--suppress CheckTagEmptyBody -->
-    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
-
     <!-- If the hardware supports specially marking packets that caused a wakeup of the
          main CPU, set this value to the mark used. -->
     <integer name="config_networkWakeupPacketMark">0</integer>
@@ -456,14 +443,6 @@
          apps requested it. -->
     <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
 
-    <!-- Configuration of network interfaces that support WakeOnLAN -->
-    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
-        <!--
-        <item>wlan0</item>
-        <item>eth0</item>
-        -->
-    </string-array>
-
     <!-- This setting is deprecated, please use
          com.android.networkstack.tethering.R.array.config_mobile_hotspot_provision_app instead. -->
     <string-array translatable="false" name="config_mobile_hotspot_provision_app">
@@ -1961,6 +1940,8 @@
     <string name="config_systemWifiCoexManager" translateable="false"></string>
     <!-- The name of the package that will hold the wellbeing role. -->
     <string name="config_systemWellbeing" translatable="false"></string>
+    <!-- The name of the package that will hold the television notification handler role -->
+    <string name="config_systemTelevisionNotificationHandler" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -2270,6 +2251,9 @@
     <bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
     <integer name="config_dozeWakeLockScreenDebounce">300</integer>
 
+    <!-- Type of the quick pickup sensor. Empty if quick pickup is not supported. -->
+    <string name="config_quickPickupSensorType" translatable="false"></string>
+
     <!-- Control whether the always on display mode is available. This should only be enabled on
          devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
          states. -->
@@ -4738,4 +4722,97 @@
 
     <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
     <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
+
+    <!-- CEC Configuration -->
+    <bool name="config_cecHdmiCecEnabled_userConfigurable">true</bool>
+    <bool name="config_cecHdmiCecControlEnabled_allowed">true</bool>
+    <bool name="config_cecHdmiCecControlEnabled_default">true</bool>
+    <bool name="config_cecHdmiCecControlDisabled_allowed">true</bool>
+    <bool name="config_cecHdmiCecControlDisabled_default">false</bool>
+
+    <bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
+    <bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
+    <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+    <bool name="config_cecHdmiCecVersion20_allowed">true</bool>
+    <bool name="config_cecHdmiCecVersion20_default">false</bool>
+
+    <bool name="config_cecSendStandbyOnSleep_userConfigurable">true</bool>
+    <bool name="config_cecPowerControlModeTv_allowed">true</bool>
+    <bool name="config_cecPowerControlModeTv_default">true</bool>
+    <bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
+    <bool name="config_cecPowerControlModeBroadcast_default">false</bool>
+    <bool name="config_cecPowerControlModeNone_allowed">true</bool>
+    <bool name="config_cecPowerControlModeNone_default">false</bool>
+
+    <bool name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_default">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+
+    <bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
+    <bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
+    <bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
+    <bool name="config_cecSystemAudioModeMutingDisabled_allowed">true</bool>
+    <bool name="config_cecSystemAudioModeMutingDisabled_default">false</bool>
+
+    <bool name="config_cecVolumeControlMode_userConfigurable">true</bool>
+    <bool name="config_cecVolumeControlModeEnabled_allowed">true</bool>
+    <bool name="config_cecVolumeControlModeEnabled_default">true</bool>
+    <bool name="config_cecVolumeControlModeDisabled_allowed">true</bool>
+    <bool name="config_cecVolumeControlModeDisabled_default">false</bool>
+
+    <bool name="config_cecTvWakeOnOneTouchPlay_userConfigurable">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayEnabled_allowed">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayEnabled_default">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayDisabled_allowed">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayDisabled_default">false</bool>
+
+    <bool name="config_cecTvSendStandbyOnSleep_userConfigurable">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepEnabled_allowed">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepEnabled_default">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool>
+
+    <bool name="config_cecRcProfileTv_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileTvNone_allowed">true</bool>
+    <bool name="config_cecRcProfileTvNone_default">true</bool>
+    <bool name="config_cecRcProfileTvOne_allowed">true</bool>
+    <bool name="config_cecRcProfileTvOne_default">false</bool>
+    <bool name="config_cecRcProfileTvTwo_allowed">true</bool>
+    <bool name="config_cecRcProfileTvTwo_default">false</bool>
+    <bool name="config_cecRcProfileTvThree_allowed">true</bool>
+    <bool name="config_cecRcProfileTvThree_default">false</bool>
+    <bool name="config_cecRcProfileTvFour_allowed">true</bool>
+    <bool name="config_cecRcProfileTvFour_default">false</bool>
+
+    <bool name="config_cecRcProfileSourceRootMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuHandled_default">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuNotHandled_default">false</bool>
+
+    <bool name="config_cecRcProfileSourceSetupMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuHandled_default">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuNotHandled_default">false</bool>
+
+    <bool name="config_cecRcProfileSourceContentsMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuHandled_default">false</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuNotHandled_default">true</bool>
+
+    <bool name="config_cecRcProfileSourceTopMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceTopMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceTopMenuHandled_default">false</bool>
+    <bool name="config_cecRcProfileSourceTopMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceTopMenuNotHandled_default">true</bool>
+
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default">false</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default">true</bool>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 3a41d5f..7bc4663 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -247,4 +247,10 @@
 
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER}. -->
   <item type="id" name="accessibilityActionImeEnter" />
+
+  <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
+  <item type="id" name="remote_views_next_child" />
+
+  <!-- View tag associating a view with its stable id for potential recycling. -->
+  <item type="id" name = "remote_views_stable_id" />
 </resources>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/core/res/res/values/policy_exempt_apps.xml
similarity index 68%
copy from packages/SystemUI/res/layout/udfps_animation_view_bp.xml
copy to core/res/res/values/policy_exempt_apps.xml
index 0cfbf2e..1cead83 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
+++ b/core/res/res/values/policy_exempt_apps.xml
@@ -14,9 +14,11 @@
   ~ 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>
+<resources>
+    <!--
+    A collection of apps that are critical for the device and hence will never be disabled by
+    device policies or APIs.
+    -->
+    <string-array translatable="false" name="policy_exempt_apps">
+    </string-array>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 293018d..0979ab55 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3071,7 +3071,6 @@
     <public name="windowSplashScreenAnimationDuration"/>
     <public name="windowSplashScreenBrandingImage"/>
     <public name="splashScreenTheme" />
-    <public name="rippleStyle" />
     <public name="maxResizeWidth" />
     <public name="maxResizeHeight" />
     <public name="targetCellWidth" />
@@ -3088,6 +3087,8 @@
     <public name="passwordsActivity"/>
     <public name="selectableAsDefault"/>
     <public name="isAccessibilityTool"/>
+    <public name="attributionTags"/>
+    <public name="suppressesSpellChecker" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
@@ -3175,6 +3176,8 @@
     <public name="config_systemWifiCoexManager" />
     <!-- @hide @SystemApi -->
     <public name="config_systemWellbeing" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemTelevisionNotificationHandler" />
   </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 4af561b..2c7f9a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -889,6 +889,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_expandStatusBar">Allows the app to expand or collapse the status bar.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_fullScreenIntent">display notifications as full screen activities on a locked device</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_fullScreenIntent">Allows the app to display notifications as full screen activities on a locked device</string>
+
     <!-- Title of an application permission, listed so the user can install application shortcuts
     in their Launcher -->
     <string name="permlab_install_shortcut">install shortcuts</string>
@@ -3691,6 +3696,8 @@
     <string name="ext_media_checking_notification_title">Checking <xliff:g id="name" example="SD card">%s</xliff:g>\u2026</string>
     <!-- Notification body when external media is being checked [CHAR LIMIT=NONE] -->
     <string name="ext_media_checking_notification_message">Reviewing current content</string>
+    <!-- TV specific notification body when external media is being checked [CHAR LIMIT=75] -->
+    <string name="ext_media_checking_notification_message" product="tv">Analyzing media storage</string>
 
     <!-- Notification body when new external media is detected [CHAR LIMIT=30] -->
     <string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3698,11 +3705,15 @@
     <string name="ext_media_new_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
     <!-- Notification body when new external media is detected [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message">Tap to set up</string>
+    <!-- TV specific notification body when new external media is detected [CHAR LIMIT=75] -->
+    <string name="ext_media_new_notification_message" product="tv">Select to set up</string>
     <!-- Automotive specific notification body when new external media is detected. [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string>
 
     <!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] -->
     <string name="ext_media_ready_notification_message">For transferring photos and media</string>
+    <!-- TV specific notification body when external media is ready for use [CHAR LIMIT=75] -->
+    <string name="ext_media_ready_notification_message" product="tv">Browse media files</string>
 
     <!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] -->
     <string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3721,8 +3732,8 @@
     <string name="ext_media_unsupported_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
     <!-- Notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string>
-    <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
-    <string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string>
+    <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=75] -->
+    <string name="ext_media_unsupported_notification_message" product="tv">Select to set up <xliff:g id="name" example="SD card">%s</xliff:g> in a supported format.</string>
     <!-- Automotive specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message" product="automotive">You may need to reformat the device</string>
 
@@ -5560,6 +5571,8 @@
     <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label">On-screen Accessibility Shortcut Chooser</string>
     <!-- Label for triggering hardware accessibility shortcut action [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
+    <!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
     <!-- Accessibility description of caption view -->
     <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
 
@@ -5873,9 +5886,9 @@
     <!-- Window magnification prompt related string. -->
 
     <!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
-    <string name="window_magnification_prompt_title">New: Window Magnifier</string>
-    <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
-    <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string>
+    <string name="window_magnification_prompt_title">Magnify part of your screen</string>
+    <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=NONE] -->
+    <string name="window_magnification_prompt_content">You can now magnify your full screen, a specific area, or switch between both options.</string>
     <!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
     <string name="turn_on_magnification_settings_action">Turn on in Settings</string>
     <!-- Notification action to dismiss. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d904f7a..b935788 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -692,7 +692,6 @@
   <java-symbol type="string" name="not_checked" />
   <java-symbol type="array" name="config_ethernet_interfaces" />
   <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" />
-  <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
   <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
   <java-symbol type="string" name="config_mms_user_agent" />
   <java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1266,6 +1265,8 @@
   <java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
   <java-symbol type="array" name="cross_profile_apps" />
   <java-symbol type="array" name="vendor_cross_profile_apps" />
+  <java-symbol type="array" name="policy_exempt_apps" />
+  <java-symbol type="array" name="vendor_policy_exempt_apps" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -1970,11 +1971,9 @@
   <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
   <java-symbol type="integer" name="config_lowBatteryWarningLevel" />
   <java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
-  <java-symbol type="integer" name="config_networkTransitionTimeout" />
   <java-symbol type="integer" name="config_networkNotifySwitchType" />
   <java-symbol type="array" name="config_networkNotifySwitches" />
   <java-symbol type="integer" name="config_networkAvoidBadWifi" />
-  <java-symbol type="string" name="config_networkCaptivePortalServerUrl" />
   <java-symbol type="integer" name="config_networkWakeupPacketMark" />
   <java-symbol type="integer" name="config_networkWakeupPacketMask" />
   <java-symbol type="bool" name="config_apfDrop802_3Frames" />
@@ -3116,6 +3115,7 @@
 
   <!-- Notifications: CallStyle -->
   <java-symbol type="layout" name="notification_template_material_call" />
+  <java-symbol type="layout" name="notification_template_material_big_call" />
   <java-symbol type="string" name="call_notification_answer_action" />
   <java-symbol type="string" name="call_notification_decline_action" />
   <java-symbol type="string" name="call_notification_hang_up_action" />
@@ -3595,6 +3595,7 @@
   <java-symbol type="string" name="config_dozeUdfpsLongPressSensorType" />
   <java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" />
   <java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" />
+  <java-symbol type="string" name="config_quickPickupSensorType" />
 
   <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
   <java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
@@ -3986,6 +3987,7 @@
   <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_label" />
   <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
   <java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
+  <java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
 
   <java-symbol type="string" name="accessibility_freeform_caption" />
 
@@ -4224,4 +4226,100 @@
   <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
 
   <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
+
+  <!-- CEC Configuration -->
+  <java-symbol type="bool" name="config_cecHdmiCecEnabled_userConfigurable" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_default" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecHdmiCecVersion_userConfigurable" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion14b_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion14b_default" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
+
+  <java-symbol type="bool" name="config_cecSendStandbyOnSleep_userConfigurable" />
+  <java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+  <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
+  <java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeNone_default" />
+
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_default" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
+
+  <java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecVolumeControlMode_userConfigurable" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_default" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlay_userConfigurable" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_default" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleep_userConfigurable" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_default" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvNone_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvOne_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvOne_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvTwo_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvTwo_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvThree_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvThree_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvFour_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvFour_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default" />
+
+  <java-symbol type="id" name="remote_views_next_child" />
+  <java-symbol type="id" name="remote_views_stable_id" />
 </resources>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/core/res/res/values/vendor_policy_exempt_apps.xml
similarity index 68%
copy from packages/SystemUI/res/layout/udfps_animation_view_bp.xml
copy to core/res/res/values/vendor_policy_exempt_apps.xml
index 0cfbf2e..eb4c760 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
+++ b/core/res/res/values/vendor_policy_exempt_apps.xml
@@ -14,9 +14,11 @@
   ~ 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>
+<resources>
+    <!--
+    A collection of apps that are critical for the device and hence will never be disabled by
+    device policies or APIs.
+    -->
+    <string-array translatable="false" name="vendor_policy_exempt_apps">
+    </string-array>
+</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
new file mode 100644
index 0000000..7ef1d5e
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.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.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+public class AppSearchSessionUnitTest {
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private final AppSearchManager mAppSearch = mContext.getSystemService(AppSearchManager.class);
+    private final Executor mExecutor = mContext.getMainExecutor();
+    private AppSearchSession mSearchSession;
+
+    @Before
+    public void setUp() throws Exception {
+        // Remove all documents from any instances that may have been created in the tests.
+        Objects.requireNonNull(mAppSearch);
+        AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder()
+                .setDatabaseName("testDb").build();
+        CompletableFuture<AppSearchResult<AppSearchSession>> future = new CompletableFuture<>();
+        mAppSearch.createSearchSession(searchContext, mExecutor, future::complete);
+        mSearchSession = future.get().getResultValue();
+
+        CompletableFuture<AppSearchResult<SetSchemaResponse>> schemaFuture =
+                new CompletableFuture<>();
+        mSearchSession.setSchema(
+                new SetSchemaRequest.Builder().setForceOverride(true).build(), mExecutor,
+                schemaFuture::complete);
+
+        schemaFuture.get().getResultValue();
+    }
+
+    @Test
+    public void testPutDocument_throwsNullException() throws Exception {
+        // Create a document
+        AppSearchEmail inEmail =
+                new AppSearchEmail.Builder("namespace", "uri1")
+                        .setFrom("from@example.com")
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+
+        // clear the document bundle to make our service crash and throw an NullPointerException.
+        inEmail.getBundle().clear();
+        CompletableFuture<AppSearchBatchResult<String, Void>> putDocumentsFuture =
+                new CompletableFuture<>();
+
+        // Index the broken document.
+        mSearchSession.put(
+                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail).build(),
+                mExecutor, new BatchResultCallback<String, Void>() {
+                    @Override
+                    public void onResult(AppSearchBatchResult<String, Void> result) {
+                        putDocumentsFuture.complete(result);
+                    }
+
+                    @Override
+                    public void onSystemError(Throwable throwable) {
+                        putDocumentsFuture.completeExceptionally(throwable);
+                    }
+                });
+
+        // Verify the NullPointException has been thrown.
+        ExecutionException executionException = expectThrows(ExecutionException.class,
+                putDocumentsFuture::get);
+        assertThat(executionException.getCause()).isInstanceOf(NullPointerException.class);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
index 119b70a..ed53d5f 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
@@ -25,7 +25,7 @@
     @Test
     public void testBuildEmailAndGetValue() {
         AppSearchEmail email =
-                new AppSearchEmail.Builder("uri")
+                new AppSearchEmail.Builder("namespace", "uri")
                         .setFrom("FakeFromAddress")
                         .setCc("CC1", "CC2")
                         // Score and Property are mixed into the middle to make sure
@@ -37,6 +37,7 @@
                         .setBody("EmailBody")
                         .build();
 
+        assertThat(email.getNamespace()).isEqualTo("namespace");
         assertThat(email.getUri()).isEqualTo("uri");
         assertThat(email.getFrom()).isEqualTo("FakeFromAddress");
         assertThat(email.getTo()).isNull();
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index af77b6c..b884ddc 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -27,13 +27,13 @@
     @Test
     public void testRecreateFromParcel() {
         GenericDocument inDoc =
-                new GenericDocument.Builder<>("uri1", "schema1")
+                new GenericDocument.Builder<>("namespace", "uri1", "schema1")
                         .setScore(42)
                         .setPropertyString("propString", "Hello")
                         .setPropertyBytes("propBytes", new byte[][] {{1, 2}})
                         .setPropertyDocument(
                                 "propDocument",
-                                new GenericDocument.Builder<>("uri2", "schema2")
+                                new GenericDocument.Builder<>("namespace", "uri2", "schema2")
                                         .setPropertyString("propString", "Goodbye")
                                         .setPropertyBytes("propBytes", new byte[][] {{3, 4}})
                                         .build())
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
index 7d175d9..7637214 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
@@ -31,8 +31,8 @@
     public void addGenericDocument_byCollection() {
         Set<AppSearchEmail> emails =
                 ImmutableSet.of(
-                        new AppSearchEmail.Builder("test1").build(),
-                        new AppSearchEmail.Builder("test2").build());
+                        new AppSearchEmail.Builder("namespace", "test1").build(),
+                        new AppSearchEmail.Builder("namespace", "test2").build());
         PutDocumentsRequest request =
                 new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
 
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index 8895f9b..b282064 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
+import android.os.UserHandle;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -86,4 +87,11 @@
                 mCp.validateIncomingUri(
                         Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar")));
     }
+
+    @Test
+    public void testCreateContentUriAsUser() {
+        Uri uri = Uri.parse("content://com.example/foo/bar");
+        Uri expectedUri = Uri.parse("content://7@com.example/foo/bar");
+        assertEquals(expectedUri, ContentProvider.createContentUriAsUser(uri, UserHandle.of(7)));
+    }
 }
diff --git a/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java b/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java
deleted file mode 100644
index 4863cfe..0000000
--- a/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.inputmethodservice;
-
-import static android.content.res.Configuration.KEYBOARD_12KEY;
-import static android.content.res.Configuration.NAVIGATION_NONAV;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.rule.ServiceTestRule;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeoutException;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class InputMethodServiceTest {
-    private InputMethodService mService;
-    private Context mContext;
-    @Rule
-    public final ServiceTestRule serviceRule = new ServiceTestRule();
-
-    @Before
-    public void setUp() throws TimeoutException {
-        mContext = getInstrumentation().getContext();
-        mService = new InputMethodService();
-    }
-
-    @Test
-    public void testShouldImeRestartForConfig() throws Exception {
-        // Make sure we preserve Pre-S behavior i.e. Service restarts.
-        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
-        Configuration config = mContext.getResources().getConfiguration();
-        mService.setLastKnownConfig(config);
-        assertTrue("IME should restart for Pre-S",
-                mService.shouldImeRestartForConfig(config));
-
-        // IME shouldn't restart on targetSdk S+ (with no config changes).
-        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
-        assertFalse("IME shouldn't restart for S+",
-                mService.shouldImeRestartForConfig(config));
-
-        // Screen density changed but IME doesn't handle congfigChanges
-        config.densityDpi = 99;
-        assertTrue("IME should restart for unhandled configChanges",
-                mService.shouldImeRestartForConfig(config));
-
-        // opt-in IME to handle config changes.
-        mService.setHandledConfigChanges(ActivityInfo.CONFIG_DENSITY);
-        assertFalse("IME shouldn't restart for S+ since it handles configChanges",
-                mService.shouldImeRestartForConfig(config));
-    }
-}
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index c06405a..09c36dd 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -16,9 +16,10 @@
 
 package android.os;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.hardware.vibrator.IVibrator;
 import android.platform.test.annotations.Presubmit;
@@ -33,72 +34,128 @@
 
     @Test
     public void testHasAmplitudeControl() {
-        assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl());
-        assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS
-                | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl());
+        VibratorInfo noCapabilities = new InfoBuilder().build();
+        assertFalse(noCapabilities.hasAmplitudeControl());
+        VibratorInfo composeAndAmplitudeControl = new InfoBuilder()
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
+                        | IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build();
+        assertTrue(composeAndAmplitudeControl.hasAmplitudeControl());
     }
 
     @Test
     public void testHasCapabilities() {
-        assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
-                .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
-        assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
-                .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
+        VibratorInfo info = new InfoBuilder()
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .build();
+        assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
+        assertFalse(info.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
     }
 
     @Test
     public void testIsEffectSupported() {
-        VibratorInfo info = new VibratorInfo(/* id= */ 0, /* capabilities= */0,
-                new int[]{VibrationEffect.EFFECT_CLICK}, null);
+        VibratorInfo noEffects = new InfoBuilder().build();
+        VibratorInfo canClick = new InfoBuilder()
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .build();
         assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
-                createInfo(/* capabilities= */ 0).isEffectSupported(VibrationEffect.EFFECT_CLICK));
+                noEffects.isEffectSupported(VibrationEffect.EFFECT_CLICK));
         assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
-                info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+                canClick.isEffectSupported(VibrationEffect.EFFECT_CLICK));
         assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
-                info.isEffectSupported(VibrationEffect.EFFECT_TICK));
+                canClick.isEffectSupported(VibrationEffect.EFFECT_TICK));
     }
 
     @Test
     public void testIsPrimitiveSupported() {
-        VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS,
-                null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+        VibratorInfo info = new InfoBuilder()
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .build();
         assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
         assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
 
         // Returns false when there is no compose capability.
-        info = new VibratorInfo(/* id= */ 0, /* capabilities= */ 0,
-                null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+        info = new InfoBuilder()
+                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .build();
         assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
     }
 
     @Test
     public void testEquals() {
-        VibratorInfo empty = new VibratorInfo(1, 0, null, null);
-        VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
-                new int[]{VibrationEffect.EFFECT_CLICK},
-                new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+        InfoBuilder completeBuilder = new InfoBuilder()
+                .setId(1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .setQFactor(2f)
+                .setResonantFrequency(150f);
+        VibratorInfo complete = completeBuilder.build();
 
         assertEquals(complete, complete);
-        assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
-                new int[]{VibrationEffect.EFFECT_CLICK},
-                new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}));
+        assertEquals(complete, completeBuilder.build());
 
-        assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{})));
-        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, IVibrator.CAP_AMPLITUDE_CONTROL,
-                new int[]{}, new int[]{})));
-        assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
-                null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
-        assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
-                new int[]{VibrationEffect.EFFECT_CLICK}, null)));
+        VibratorInfo completeWithComposeControl = completeBuilder
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .build();
+        assertNotEquals(complete, completeWithComposeControl);
+
+        VibratorInfo completeWithNoEffects = completeBuilder
+                .setSupportedEffects()
+                .setSupportedPrimitives()
+                .build();
+        assertNotEquals(complete, completeWithNoEffects);
+
+        VibratorInfo completeWithUnknownEffects = completeBuilder
+                .setSupportedEffects(null)
+                .build();
+        assertNotEquals(complete, completeWithNoEffects);
+
+        VibratorInfo completeWithUnknownPrimitives = completeBuilder
+                .setSupportedPrimitives(null)
+                .build();
+        assertNotEquals(complete, completeWithUnknownPrimitives);
+
+        VibratorInfo completeWithDifferentF0 = completeBuilder
+                .setResonantFrequency(complete.getResonantFrequency() + 3f)
+                .build();
+        assertNotEquals(complete, completeWithDifferentF0);
+
+        VibratorInfo completeWithUnknownF0 = completeBuilder
+                .setResonantFrequency(Float.NaN)
+                .build();
+        assertNotEquals(complete, completeWithUnknownF0);
+
+        VibratorInfo completeWithUnknownQFactor = completeBuilder
+                .setQFactor(Float.NaN)
+                .build();
+        assertNotEquals(complete, completeWithUnknownQFactor);
+
+        VibratorInfo completeWithDifferentQFactor = completeBuilder
+                .setQFactor(complete.getQFactor() + 3f)
+                .build();
+        assertNotEquals(complete, completeWithDifferentQFactor);
+
+        VibratorInfo empty = new InfoBuilder().setId(1).build();
+        VibratorInfo emptyWithKnownSupport = new InfoBuilder()
+                .setId(1)
+                .setSupportedEffects()
+                .setSupportedPrimitives()
+                .build();
+        assertNotEquals(empty, emptyWithKnownSupport);
     }
 
     @Test
-    public void testSerialization() {
-        VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
-                new int[]{VibrationEffect.EFFECT_CLICK}, null);
+    public void testParceling() {
+        VibratorInfo original = new InfoBuilder()
+                .setId(1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+                .setSupportedPrimitives(null)
+                .setResonantFrequency(1.3f)
+                .setQFactor(Float.NaN)
+                .build();
 
         Parcel parcel = Parcel.obtain();
         original.writeToParcel(parcel, 0);
@@ -107,7 +164,47 @@
         assertEquals(original, restored);
     }
 
-    private static VibratorInfo createInfo(long capabilities) {
-        return new VibratorInfo(/* id= */ 0, capabilities, null, null);
+    private static class InfoBuilder {
+        private int mId = 0;
+        private int mCapabilities = 0;
+        private int[] mSupportedEffects = null;
+        private int[] mSupportedPrimitives = null;
+        private float mResonantFrequency = Float.NaN;
+        private float mQFactor = Float.NaN;
+
+        public InfoBuilder setId(int id) {
+            mId = id;
+            return this;
+        }
+
+        public InfoBuilder setCapabilities(int capabilities) {
+            mCapabilities = capabilities;
+            return this;
+        }
+
+        public InfoBuilder setSupportedEffects(int... supportedEffects) {
+            mSupportedEffects = supportedEffects;
+            return this;
+        }
+
+        public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) {
+            mSupportedPrimitives = supportedPrimitives;
+            return this;
+        }
+
+        public InfoBuilder setResonantFrequency(float resonantFrequency) {
+            mResonantFrequency = resonantFrequency;
+            return this;
+        }
+
+        public InfoBuilder setQFactor(float qFactor) {
+            mQFactor = qFactor;
+            return this;
+        }
+
+        public VibratorInfo build() {
+            return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
+                    mResonantFrequency, mQFactor);
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 80165f0..fa1aa5e 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -9,3 +9,6 @@
 per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
index 8eb13bc..4349021 100644
--- a/core/tests/coretests/src/android/view/RoundedCornerTest.java
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -62,6 +62,13 @@
     }
 
     @Test
+    public void testIsEmpty_negativeCenter() {
+        RoundedCorner roundedCorner =
+                new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT, 1, -2, -3);
+        assertThat(roundedCorner.isEmpty(), is(true));
+    }
+
+    @Test
     public void testEquals() {
         RoundedCorner roundedCorner = new RoundedCorner(
                 RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index f6e02bc..28f9ccc 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -41,7 +41,9 @@
 
 import static org.hamcrest.Matchers.is;
 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 android.content.res.TypedArray;
 import android.text.Selection;
@@ -356,4 +358,71 @@
                     .perform(clearText());
         }
     }
+
+    @Test
+    public void testCursorVisibility() {
+        final TextView textView = getActivity().findViewById(R.id.textview);
+        final String text = "abc";
+
+        assertTrue(textView.isCursorVisible());
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1);
+        showSuggestionsPopup();
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("ABC");
+        assertFalse(textView.isCursorVisible());
+
+        // Delete an item.
+        clickSuggestionsPopupItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+        assertSuggestionsPopupIsNotDisplayed();
+        assertTrue(textView.isCursorVisible());
+    }
+
+    @Test
+    public void testCursorVisibilityWhenImeConsumesInput() {
+        final TextView textView = getActivity().findViewById(R.id.textview);
+        final String text = "abc";
+
+        assertTrue(textView.isCursorVisible());
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        setImeConsumesInputWithExpect(textView, true /* imeConsumesInput */,
+                false /* expectedCursorVisibility */);
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1);
+        showSuggestionsPopup();
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("ABC");
+        assertFalse(textView.isCursorVisible());
+
+        // Delete an item.
+        clickSuggestionsPopupItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+        assertSuggestionsPopupIsNotDisplayed();
+        assertFalse(textView.isCursorVisible());
+
+        // Set IME not consumes input, cursor should be back to visible.
+        setImeConsumesInputWithExpect(textView, false /* imeConsumesInput */,
+                true /* expectedCursorVisibility */);
+    }
+
+    private void setImeConsumesInputWithExpect(
+            final TextView textView, boolean imeConsumesInput, boolean expectedCursorVisibility) {
+        textView.post(() -> textView.setImeConsumesInput(imeConsumesInput));
+        getInstrumentation().waitForIdleSync();
+        if (expectedCursorVisibility) {
+            assertTrue(textView.isCursorVisible());
+        } else {
+            assertFalse(textView.isCursorVisible());
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 7088890..10ff3a4 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -19,18 +19,22 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.power.MeasuredEnergyStats;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -81,6 +85,10 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        final boolean[] supportedPowerBuckets =
+                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+
         mStatsRule.getBatteryStats()
                 .setUserInfoProvider(mMockUserInfoProvider)
                 .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
@@ -88,7 +96,8 @@
                 .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
                 .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
                 .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
-                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
+                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0);
     }
 
     @Test
@@ -103,28 +112,28 @@
 
         // User/System CPU time
         doAnswer(invocation -> {
-            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
             // User/system time in microseconds
             callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
             callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
             return null;
-        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any());
+        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
 
         // Active CPU time
         doAnswer(invocation -> {
-            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
             callback.onUidCpuTime(APP_UID1, 1111L);
             callback.onUidCpuTime(APP_UID2, 3333L);
             return null;
-        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
+        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
 
         // Per-cluster CPU time
         doAnswer(invocation -> {
-            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
             callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
             callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
             return null;
-        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
+        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
 
         mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
 
@@ -134,7 +143,8 @@
         CpuPowerCalculator calculator =
                 new CpuPowerCalculator(mStatsRule.getPowerProfile());
 
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
         UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
         assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
@@ -150,4 +160,64 @@
                 .isWithin(PRECISION).of(2.672322);
         assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
     }
+
+    @Test
+    public void testMeasuredEnergyBasedModel() {
+        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
+
+        when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+        when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
+
+        when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+
+        // User/System CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+            // User/system time in microseconds
+            callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
+            return null;
+        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
+
+        // Active CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, 1111L);
+            callback.onUidCpuTime(APP_UID2, 3333L);
+            return null;
+        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+
+        // Per-cluster CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+            return null;
+        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
+
+        final long[] clusterChargesUC = new long[]{13577531, 24688642};
+        mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC);
+
+        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
+        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
+
+        CpuPowerCalculator calculator =
+                new CpuPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+        assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+                .isEqualTo(3333);
+        assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+                .isWithin(PRECISION).of(3.18877);
+        assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
+
+        UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+        assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+                .isEqualTo(7777);
+        assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+                .isWithin(PRECISION).of(7.44072);
+        assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/view/OWNERS b/core/tests/coretests/src/com/android/internal/view/OWNERS
new file mode 100644
index 0000000..1dad10d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/OWNERS
@@ -0,0 +1,3 @@
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
similarity index 80%
rename from core/tests/mockingcoretests/src/android/view/DisplayTests.java
rename to core/tests/mockingcoretests/src/android/view/DisplayTest.java
index 678f21f..f8dd153 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -56,12 +56,15 @@
  *
  * <p>Build/Install/Run:
  *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
+ * atest FrameworksMockingCoreTests:android.view.DisplayTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
-public class DisplayTests {
+public class DisplayTest {
 
     private static final int APP_WIDTH = 272;
     private static final int APP_HEIGHT = 700;
@@ -73,6 +76,11 @@
     private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
     private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
 
+    // Bounds of the device.
+    private static Rect sDeviceBoundsPortrait = new Rect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT);
+    private static Rect sDeviceBoundsLandscape = new Rect(0, 0, LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+
     private StaticMockitoSession mMockitoSession;
 
     private DisplayManagerGlobal mDisplayManagerGlobal;
@@ -278,29 +286,57 @@
     }
 
     @Test
-    public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
+    public void testGetRealSize_resourcesPortraitSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
+        verifyRealSizeMatchesBounds(display, sAppBoundsPortrait);
     }
 
     @Test
-    public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+    public void testGetRealSize_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is not rotated.
+        setDisplayInfoPortrait(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealSizeMatchesBounds(display, sDeviceBoundsPortrait);
+    }
+
+    @Test
+    public void testGetRealSize_resourcesLandscapeSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is rotated.
         setDisplayInfoLandscape(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
+        verifyRealSizeMatchesBounds(display, sAppBoundsLandscape);
+    }
+
+    @Test
+    public void testGetRealSize_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is rotated.
+        setDisplayInfoLandscape(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealSizeMatchesBounds(display, sDeviceBoundsLandscape);
     }
 
     @Test
@@ -396,29 +432,57 @@
     }
 
     @Test
-    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
+    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
+        verifyRealMetricsMatchesBounds(display, sAppBoundsPortrait);
     }
 
     @Test
-    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is not rotated.
+        setDisplayInfoPortrait(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealMetricsMatchesBounds(display, sDeviceBoundsPortrait);
+    }
+
+    @Test
+    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is rotated.
         setDisplayInfoLandscape(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
+        verifyRealMetricsMatchesBounds(display, sAppBoundsLandscape);
+    }
+
+    @Test
+    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is rotated.
+        setDisplayInfoLandscape(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealMetricsMatchesBounds(display, sDeviceBoundsLandscape);
     }
 
     // Given rotated display dimensions, calculate the letterboxed app bounds.
@@ -450,8 +514,8 @@
      * Set max bounds to be sandboxed to the app bounds, indicating the app is in
      * size compat mode or letterbox.
      */
-    private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
-        resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
+    private static void setMaxBoundsSandboxed(Resources resources, Rect bounds) {
+        resources.getConfiguration().windowConfiguration.setMaxBounds(bounds);
     }
 
     /**
@@ -492,17 +556,17 @@
         assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
     }
 
-    private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
+    private static void verifyRealSizeMatchesBounds(Display display, Rect bounds) {
         Point size = new Point();
         display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
+        assertThat(size).isEqualTo(new Point(bounds.width(), bounds.height()));
     }
 
-    private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
+    private static void verifyRealMetricsMatchesBounds(Display display, Rect bounds) {
         DisplayMetrics metrics = new DisplayMetrics();
         display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
-        assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
+        assertThat(metrics.widthPixels).isEqualTo(bounds.width());
+        assertThat(metrics.heightPixels).isEqualTo(bounds.height());
     }
 
     private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index c01bb75..e41805d 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -32,6 +31,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.util.concurrent.Executor;
 
@@ -42,51 +42,23 @@
 @RunWith(AndroidJUnit4.class)
 public class RangingManagerTest {
 
-    private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
     private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
     private static final PersistableBundle PARAMS = new PersistableBundle();
     private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
 
     @Test
     public void testOpenSession_OpenRangingInvoked() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
-        verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS));
-    }
-
-    @Test
-    public void testOpenSession_ErrorIfSameSessionHandleReturned() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
-        RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
-
-        // Calling openSession will cause the same session handle to be returned. The onClosed
-        // callback should be invoked
-        RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback2);
-        verify(callback, times(0)).onClosed(anyInt(), any());
-        verify(callback2, times(1)).onClosed(anyInt(), any());
-    }
-
-    @Test
-    public void testOnRangingOpened_ValidSessionHandle() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
-        RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
-        rangingManager.onRangingOpened(handle);
-        verify(callback, times(1)).onOpened(any());
+        verify(adapter, times(1)).openRanging(any(), eq(rangingManager), eq(PARAMS));
     }
 
     @Test
     public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
 
         rangingManager.onRangingOpened(new SessionHandle(2));
@@ -95,18 +67,20 @@
 
     @Test
     public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
-
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        RangingManager rangingManager = new RangingManager(adapter);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingOpened(sessionHandle1);
         verify(callback1, times(1)).onOpened(any());
@@ -119,12 +93,17 @@
 
     @Test
     public void testCorrectCallbackInvoked() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle handle = sessionHandleCaptor.getValue();
+
         rangingManager.onRangingOpened(handle);
         verify(callback, times(1)).onOpened(any());
 
@@ -156,20 +135,23 @@
 
     @Test
     public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         // Verify that if multiple sessions are registered, only the session that is
         // requested to close receives the associated callbacks
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
+        RangingManager rangingManager = new RangingManager(adapter);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        RangingManager rangingManager = new RangingManager(ADAPTER);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
         verify(callback1, times(1)).onClosed(anyInt(), any());
@@ -182,19 +164,22 @@
 
     @Test
     public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        RangingManager rangingManager = new RangingManager(adapter);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.onRangingStarted(sessionHandle1, PARAMS);
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
         rangingManager.onRangingStarted(sessionHandle2, PARAMS);
 
         rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
@@ -232,17 +217,24 @@
 
     private void runReason(@RangingChangeReason int reasonIn,
             @RangingSession.Callback.Reason int reasonOut) throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle handle = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
         verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
 
         // Open a new session
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        handle = sessionHandleCaptor.getValue();
         rangingManager.onRangingOpened(handle);
 
         rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index 8e7f7c56..75c6924 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -16,34 +16,23 @@
 
 package android.uwb;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.SystemClock;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.Executor;
 
 public class UwbTestUtils {
     private UwbTestUtils() {}
 
-    public static boolean isUwbSupported(Context context) {
-        PackageManager packageManager = context.getPackageManager();
-        return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB);
-    }
-
     public static AngleMeasurement getAngleMeasurement() {
-        return new AngleMeasurement.Builder()
-                .setRadians(getDoubleInRange(-Math.PI, Math.PI))
-                .setErrorRadians(getDoubleInRange(0, Math.PI))
-                .setConfidenceLevel(getDoubleInRange(0, 1))
-                .build();
+        return new AngleMeasurement(
+                getDoubleInRange(-Math.PI, Math.PI),
+                getDoubleInRange(0, Math.PI),
+                getDoubleInRange(0, 1));
     }
 
     public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
-        return new AngleOfArrivalMeasurement.Builder()
+        return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
                 .setAltitude(getAngleMeasurement())
-                .setAzimuth(getAngleMeasurement())
                 .build();
     }
 
@@ -69,14 +58,6 @@
                 .build();
     }
 
-    public static List<RangingMeasurement> getRangingMeasurements(int num) {
-        List<RangingMeasurement> result = new ArrayList<>();
-        for (int i = 0; i < num; i++) {
-            result.add(getRangingMeasurement());
-        }
-        return result;
-    }
-
     public static RangingReport getRangingReports(int numMeasurements) {
         RangingReport.Builder builder = new RangingReport.Builder();
         for (int i = 0; i < numMeasurements; i++) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 77a38a9..b171599 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -154,6 +154,8 @@
     <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" />
     <assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" />
+    <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" />
+    <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="media" />
 
     <assign-permission name="android.permission.INTERNET" uid="media" />
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c49fe85..a7b6636 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -488,6 +488,8 @@
         <permission name="android.permission.MANAGE_UI_TRANSLATION" />
         <!-- Permission required for CTS test - ClipboardManagerTest -->
         <permission name="android.permission.SET_CLIP_SOURCE" />
+        <!-- Permission required for CTS test - FontManagerTest -->
+        <permission name="android.permission.UPDATE_FONTS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 43d5662..4a3bd99 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -307,6 +307,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1777196134": {
+      "message": "goodToGo(): No apps to animate, mPendingAnimations=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+      "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+    },
     "-1770075711": {
       "message": "Adding window client %s that is dead, aborting.",
       "level": "WARN",
@@ -1987,12 +1993,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "194124419": {
-      "message": "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_REMOTE_ANIMATIONS",
-      "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
-    },
     "200829729": {
       "message": "ScreenRotationAnimation onAnimationEnd",
       "level": "DEBUG",
@@ -2089,6 +2089,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DragState.java"
     },
+    "269976641": {
+      "message": "goodToGo(): Animation canceled already",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+      "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+    },
     "274773837": {
       "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL transit=%s Callers=%s",
       "level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0957_Product_0001.idc b/data/keyboards/Vendor_0957_Product_0001.idc
index e1f4346..39479ce 100644
--- a/data/keyboards/Vendor_0957_Product_0001.idc
+++ b/data/keyboards/Vendor_0957_Product_0001.idc
@@ -19,5 +19,4 @@
 
 # Basic Parameters
 keyboard.layout = Vendor_0957_Product_0001
-keyboard.characterMap = Vendor_0957_Product_0001
 audio.mic = 1
\ No newline at end of file
diff --git a/data/keyboards/Vendor_248a_Product_8266.idc b/data/keyboards/Vendor_248a_Product_8266.idc
new file mode 100644
index 0000000..3021655
--- /dev/null
+++ b/data/keyboards/Vendor_248a_Product_8266.idc
@@ -0,0 +1,24 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Input Device Configuration file for Google Reference RCU Remote.
+#
+#
+
+# Basic Parameters
+# Due to a memory error on early prototypes of the reference remote control
+# the VID/PID is mapped to 248a/8266 instead of 0957/0001
+keyboard.layout = Vendor_0957_Product_0001
+audio.mic = 1
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c807882..2a6bbf3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,6 +25,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UiThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
@@ -169,6 +170,21 @@
             Collections.emptyMap();
 
     /**
+     * Returns the shared memory that used for creating Typefaces.
+     *
+     * @return A SharedMemory used for creating Typeface. Maybe null if the lazy initialization is
+     *         disabled or inside SystemServer or Zygote.
+     * @hide
+     */
+    @TestApi
+    public static @Nullable SharedMemory getSystemFontMapSharedMemory() {
+        if (ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+            Objects.requireNonNull(sSystemFontMapSharedMemory);
+        }
+        return sSystemFontMapSharedMemory;
+    }
+
+    /**
      * @hide
      */
     @UnsupportedAppUsage
@@ -1196,8 +1212,13 @@
         }
     }
 
-    /** @hide */
-    public static SharedMemory serializeFontMap(Map<String, Typeface> fontMap)
+    /**
+     * Create a serialized system font mappings.
+     *
+     * @hide
+     */
+    @TestApi
+    public static @NonNull SharedMemory serializeFontMap(@NonNull Map<String, Typeface> fontMap)
             throws IOException, ErrnoException {
         long[] nativePtrs = new long[fontMap.size()];
         // The name table will not be large, so let's create a byte array in memory.
@@ -1229,9 +1250,14 @@
     }
 
     // buffer's byte order should be BIG_ENDIAN.
-    /** @hide */
-    @VisibleForTesting
-    public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
+    /**
+     * Deserialize the font mapping from the serialized byte buffer.
+     *
+     * @hide
+     */
+    @TestApi
+    public static @NonNull Map<String, Typeface> deserializeFontMap(@NonNull ByteBuffer buffer)
+            throws IOException {
         Map<String, Typeface> fontMap = new ArrayMap<>();
         int typefacesBytesCount = buffer.getInt();
         long[] nativePtrs = nativeReadTypefaces(buffer.slice());
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 7fd3cdc..d6bbee9 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -110,17 +110,6 @@
  * </pre>
  *
  * @attr ref android.R.styleable#RippleDrawable_color
- *
- * To change the ripple style, assign the value of "solid" or "patterned" to the android:rippleStyle
- * attribute.
- *
- * <pre>
- * <code>&lt;!-- A red ripple masked against an opaque rectangle. --/>
- * &lt;ripple android:rippleStyle="patterned">
- * &lt;/ripple></code>
- * </pre>
- *
- * @attr ref android.R.styleable#RippleDrawable_rippleStyle
  */
 public class RippleDrawable extends LayerDrawable {
     /**
@@ -132,12 +121,14 @@
     /**
      * Ripple style where a solid circle is drawn. This is also the default style
      * @see #setRippleStyle(int)
+     * @hide
      */
     public static final int STYLE_SOLID = 0;
     /**
      * Ripple style where a circle shape with a patterned,
      * noisy interior expands from the hotspot to the bounds".
      * @see #setRippleStyle(int)
+     * @hide
      */
     public static final int STYLE_PATTERNED = 1;
 
@@ -1248,6 +1239,7 @@
      * @see #STYLE_PATTERNED
      *
      * @param style The style of the ripple
+     * @hide
      */
     public void setRippleStyle(@RippleStyle int style) throws IllegalArgumentException {
         if (style == STYLE_SOLID || style == STYLE_PATTERNED) {
@@ -1260,6 +1252,7 @@
     /**
      * Get the current ripple style
      * @return Ripple style
+     * @hide
      */
     public @RippleStyle int getRippleStyle() {
         return mState.mRippleStyle;
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 2549277..657a32c 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -61,53 +61,6 @@
             + "  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n"
             + "}\n"
             + "\n"
-            + "float softRing(vec2 uv, vec2 xy, float radius, float blur) {\n"
-            + "  float thickness = 0.4;\n"
-            + "  float circle_outer = softCircle(uv, xy, radius + thickness * 0.5, blur);\n"
-            + "  float circle_inner = softCircle(uv, xy, radius - thickness * 0.5, blur);\n"
-            + "  return circle_outer - circle_inner;\n"
-            + "}\n"
-            + "\n"
-            + "struct Viewport {\n"
-            + "  float aspect;\n"
-            + "  vec2 uv;\n"
-            + "  vec2 resolution_pixels;\n"
-            + "};\n"
-            + "\n"
-            + "Viewport getViewport(vec2 frag_coord, vec2 resolution_pixels) {\n"
-            + "  Viewport v;\n"
-            + "  v.aspect = resolution_pixels.y / resolution_pixels.x;\n"
-            + "  v.uv = frag_coord / resolution_pixels;\n"
-            + "  v.uv.y = (1.0 - v.uv.y) * v.aspect;\n"
-            + "  v.resolution_pixels = resolution_pixels;\n"
-            + "  return v;\n"
-            + "}\n"
-            + "\n"
-            + "vec2 getTouch(vec2 touch_position_pixels, Viewport viewport) {\n"
-            + "  vec2 touch = touch_position_pixels / viewport.resolution_pixels;\n"
-            + "  touch.y *= viewport.aspect;\n"
-            + "  return touch;\n"
-            + "}\n"
-            + "\n"
-            + "struct Wave {\n"
-            + "  float ring;\n"
-            + "  float circle;\n"
-            + "};\n"
-            + "\n"
-            + "Wave getWave(Viewport viewport, vec2 touch, float progress) {\n"
-            + "  float fade = pow((clamp(progress, 0.8, 1.0)), 8.);\n"
-            + "  Wave w;\n"
-            + "  w.ring = max(softRing(viewport.uv, touch, progress, 0.45) - fade, 0.);\n"
-            + "  w.circle = softCircle(viewport.uv, touch, 2.0 * progress, 0.2) - progress;\n"
-            + "  return w;\n"
-            + "}\n"
-            + "\n"
-            + "vec4 getRipple(vec4 color, float loudness, float sparkle, Wave wave) {\n"
-            + "  float alpha = wave.ring * sparkle * loudness\n"
-            + "        + wave.circle * color.a;\n"
-            + "  return vec4(color.rgb, saturate(alpha));\n"
-            + "}\n"
-            + "\n"
             + "float getRingMask(vec2 frag, vec2 center, float r, float progress) {\n"
             + "      float dist = distance(frag, center);\n"
             + "      float expansion = r * .6;\n"
@@ -126,19 +79,15 @@
             + "    float fadeIn = subProgress(0., 0.175, in_progress);\n"
             + "    float fadeOutNoise = subProgress(0.375, 1., in_progress);\n"
             + "    float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n"
-            + "    Viewport vp = getViewport(p, in_resolution);\n"
-            + "    vec2 touch = getTouch(in_origin, vp);\n"
-            + "    Wave w = getWave(vp, touch, in_progress * 0.25);\n"
             + "    float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n"
             + "    float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
             + "    float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n"
             + "        * ring * alpha;\n"
-            + "    vec4 r = getRipple(in_color, 1., sparkle, w);\n"
             + "    float fade = min(fadeIn, 1.-fadeOutRipple);\n"
-            + "    vec4 circle = vec4(in_color.rgb, softCircle(p, in_origin, in_maxRadius "
-            + "      * fadeIn, 0.2) * fade * in_color.a);\n"
+            + "    vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius "
+            + "      * fadeIn, 0.2) * fade);\n"
             + "    float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
-            + "    return mix(circle, vec4(1.), sparkle * mask);\n"
+            + "    return mix(circle, vec4(sparkle), sparkle) * mask;\n"
             + "}";
     private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
 
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index af49619..917eef2 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -183,6 +183,23 @@
         return nIsPostScriptType1Font(buffer, index);
     }
 
+    /**
+     * Analyze the file content and returns 1 if the font file is an OpenType collection file, 0 if
+     * the font file is a OpenType font file, -1 otherwise.
+     */
+    public static int isCollectionFont(@NonNull ByteBuffer buffer) {
+        ByteBuffer copied = buffer.slice();
+        copied.order(ByteOrder.BIG_ENDIAN);
+        int magicNumber = copied.getInt(0);
+        if (magicNumber == TTC_TAG) {
+            return 1;
+        } else if (magicNumber == SFNT_VERSION_1 || magicNumber == SFNT_VERSION_OTTO) {
+            return 0;
+        } else {
+            return -1;
+        }
+    }
+
     @FastNative
     private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
             @IntRange(from = 0) int index);
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index ed789f0..35b1c16 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.security.usermanager.IKeystoreUserManager;
+import android.security.maintenance.IKeystoreMaintenance;
 import android.system.keystore2.Domain;
 import android.system.keystore2.ResponseCode;
 import android.util.Log;
@@ -34,9 +34,9 @@
 
     public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
 
-    private static IKeystoreUserManager getService() {
-        return IKeystoreUserManager.Stub.asInterface(
-                ServiceManager.checkService("android.security.usermanager"));
+    private static IKeystoreMaintenance getService() {
+        return IKeystoreMaintenance.Stub.asInterface(
+                ServiceManager.checkService("android.security.maintenance"));
     }
 
     /**
@@ -121,4 +121,22 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Queries user state from Keystore 2.0.
+     *
+     * @param userId - Android user id of the user.
+     * @return UserState enum variant as integer if successful or an error
+     */
+    public static int getState(int userId) {
+        try {
+            return getService().getState(userId);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "getState failed", e);
+            return e.errorCode;
+        } catch (Exception e) {
+            Log.e(TAG, "Can not connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
 }
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 11cb2b7..7c80f70 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -601,7 +601,7 @@
     }
 
     /**
-     * Check whether the caller is the credential management app {@link CredentialManagementApp}.
+     * Check whether the caller is the credential management app {@code CredentialManagementApp}.
      * The credential management app has the ability to manage the user's KeyChain credentials
      * on unmanaged devices.
      *
@@ -611,6 +611,7 @@
      *
      * @return {@code true} if the caller is the credential management app.
      */
+    @WorkerThread
     public static boolean isCredentialManagementApp(@NonNull Context context) {
         boolean isCredentialManagementApp = false;
         try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
@@ -634,6 +635,7 @@
      * @return the credential management app's authentication policy.
      * @throws SecurityException if the caller is not the credential management app.
      */
+    @WorkerThread
     @NonNull
     public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy(
             @NonNull Context context) throws SecurityException {
@@ -665,6 +667,7 @@
      * @hide
      */
     @TestApi
+    @WorkerThread
     @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
     public static boolean setCredentialManagementApp(@NonNull Context context,
             @NonNull String packageName, @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
@@ -680,13 +683,21 @@
     }
 
     /**
-     * Remove the user's KeyChain credentials on unmanaged devices.
+     * Called by the credential management app {@code CredentialManagementApp} to unregister as
+     * the credential management app and stop managing the user's credentials.
+     *
+     * <p> All credentials previously installed by the credential management app will be removed
+     * from the user's device.
+     *
+     * <p> An app holding {@code MANAGE_CREDENTIAL_MANAGEMENT_APP} permission can also call this
+     * method to remove the current credential management app, even if it's not the current
+     * credential management app itself.
      *
      * @return {@code true} if the credential management app was successfully removed.
-     * @hide
      */
-    @TestApi
-    @RequiresPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP)
+    @WorkerThread
+    @RequiresPermission(value = Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
+            conditional = true)
     public static boolean removeCredentialManagementApp(@NonNull Context context) {
         try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
             keyChainConnection.getService().removeCredentialManagementApp();
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 93658e6..937f01c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeystoreResponse;
 import android.security.keystore.UserNotAuthenticatedException;
+import android.security.maintenance.UserState;
 import android.system.keystore2.Domain;
 import android.util.Log;
 
@@ -196,6 +197,19 @@
     public State state(int userId) {
         final int ret;
         try {
+            if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+                int userState = AndroidKeyStoreMaintenance.getState(userId);
+                switch (userState) {
+                    case UserState.UNINITIALIZED:
+                        return KeyStore.State.UNINITIALIZED;
+                    case UserState.LSKF_UNLOCKED:
+                        return KeyStore.State.UNLOCKED;
+                    case UserState.LSKF_LOCKED:
+                        return KeyStore.State.LOCKED;
+                    default:
+                        throw new AssertionError(KeyStore.VALUE_CORRUPTED);
+                }
+            }
             ret = mBinder.getState(userId);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index f48da74..cd77d9c 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -22,7 +22,6 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Build;
-import android.security.KeyStore;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keymaster.KeymasterDefs;
@@ -32,9 +31,14 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.nio.charset.StandardCharsets;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.SecureRandom;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.spec.ECGenParameterSpec;
 import java.util.Collection;
+import java.util.Random;
 import java.util.Set;
 
 /**
@@ -223,24 +227,49 @@
     @NonNull public static X509Certificate[] attestDeviceIds(Context context,
             @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
             DeviceIdAttestationException {
-        final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId(
-                context, idTypes, attestationChallenge);
+        String keystoreAlias = generateRandomAlias();
+        KeyGenParameterSpec.Builder builder =
+                new KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_SIGN)
+                        .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                        .setDigests(KeyProperties.DIGEST_SHA256)
+                        .setAttestationChallenge(attestationChallenge);
 
-        // Perform attestation.
-        final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
-        final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain);
-        if (errorCode != KeyStore.NO_ERROR) {
-            throw new DeviceIdAttestationException("Unable to perform attestation",
-                    KeyStore.getKeyStoreException(errorCode));
+        if (idTypes != null) {
+            builder.setAttestationIds(idTypes);
+            builder.setDevicePropertiesAttestationIncluded(true);
         }
 
         try {
-            return parseCertificateChain(outChain);
-        } catch (KeyAttestationException e) {
-            throw new DeviceIdAttestationException(e.getMessage(), e);
+            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+                    KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+            keyPairGenerator.initialize(builder.build());
+            keyPairGenerator.generateKeyPair();
+
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+
+            X509Certificate[] certificateChain =
+                    (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias);
+
+            keyStore.deleteEntry(keystoreAlias);
+
+            return certificateChain;
+        } catch (Exception e) {
+            throw new DeviceIdAttestationException("Unable to perform attestation", e);
         }
     }
 
+    private static String generateRandomAlias() {
+        Random random = new SecureRandom();
+        StringBuilder builder = new StringBuilder();
+        // Pick random uppercase letters, A-Z.  20 of them gives us ~94 bits of entropy, which
+        // should prevent any conflicts with app-selected aliases, even for very unlucky users.
+        for (int i = 0; i < 20; ++i) {
+            builder.append(random.nextInt(26) + 'A');
+        }
+        return builder.toString();
+    }
+
     /**
      * Returns true if the attestation chain provided is a valid key attestation chain.
      * @hide
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 72735a7..5cb2c3b 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -467,8 +467,8 @@
      *
      * @return The numeric namespace as configured in the keystore2_key_contexts files of Android's
      *         SEPolicy.
-     *         TODO b/171806779 link to public Keystore 2.0 documentation.
-     *              See bug for more details for now.
+     *         See <a href="https://source.android.com/security/keystore#access-control">
+     *             Keystore 2.0 access control</a>
      * @hide
      */
     @SystemApi
@@ -1042,9 +1042,9 @@
          * keys between system and vendor components, e.g., WIFI settings and WPA supplicant.
          *
          * @param namespace Numeric SELinux namespace as configured in keystore2_key_contexts
-         *                  of Android's SEPolicy.
-         *                  TODO b/171806779 link to public Keystore 2.0 documentation.
-         *                       See bug for more details for now.
+         *         of Android's SEPolicy.
+         *         See <a href="https://source.android.com/security/keystore#access-control">
+         *             Keystore 2.0 access control</a>
          * @return this Builder object.
          *
          * @hide
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d36695b..fa852e3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -340,11 +340,11 @@
      * @param keyStore The keystore2 backend.
      * @param alias The alias of the key in the Keystore database.
      * @param namespace The a Keystore namespace. This is used by system api only to request
-     *                  Android system specific keystore namespace, which can be configured
-     *                  in the device's SEPolicy. Third party apps and most system components
-     *                  set this parameter to -1 to indicate their application specific namespace.
-     *                  TODO b/171806779 link to public Keystore 2.0 documentation.
-     *                       See bug for more details for now.
+     *         Android system specific keystore namespace, which can be configured
+     *         in the device's SEPolicy. Third party apps and most system components
+     *         set this parameter to -1 to indicate their application specific namespace.
+     *         See <a href="https://source.android.com/security/keystore#access-control">
+     *             Keystore 2.0 access control</a>
      * @hide
      **/
     @NonNull
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index c0bc73d..d2b3cf6 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -18,4 +18,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.wm.shell">
     <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
 </manifest>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 2419865..c2f591b 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -51,4 +51,10 @@
 
     <!-- maximum animation duration for the icon when entering the starting window -->
     <integer name="max_starting_window_intro_icon_anim_duration">1000</integer>
+
+    <!-- Animation duration when exit starting window: icon going away -->
+    <integer name="starting_window_icon_exit_anim_duration">166</integer>
+
+    <!-- Animation duration when exit starting window: reveal app -->
+    <integer name="starting_window_app_reveal_anim_duration">333</integer>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 75bed37..3ced8d3 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -188,4 +188,13 @@
 
     <!-- The height of the brand image on staring surface. -->
     <dimen name="starting_surface_brand_image_height">80dp</dimen>
+
+    <!-- The length of the shift of main window when exit starting window. -->
+    <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
+
+    <!-- The distance of the shift icon when normal exit starting window. -->
+    <dimen name="starting_surface_normal_exit_icon_distance">120dp</dimen>
+
+    <!-- The distance of the shift icon when early exit starting window. -->
+    <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index cb54021..3708e15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -119,7 +119,7 @@
                 listeners.get(i).onDisplayAreaAppeared(displayAreaInfo);
             }
         }
-        applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+        applyConfigChangesToContext(displayAreaInfo);
     }
 
     @Override
@@ -161,24 +161,27 @@
                 listeners.get(i).onDisplayAreaInfoChanged(displayAreaInfo);
             }
         }
-        applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+        applyConfigChangesToContext(displayAreaInfo);
     }
 
     /**
-     * Applies the {@link Configuration} to the {@link DisplayAreaContext} specified by
-     * {@code displayId}.
-     *
-     * @param displayId The ID of the {@link Display} which the {@link DisplayAreaContext} is
-     *                  associated with
-     * @param newConfig The propagated configuration
+     * Applies the {@link DisplayAreaInfo} to the {@link DisplayAreaContext} specified by
+     * {@link DisplayAreaInfo#displayId}.
      */
-    private void applyConfigChangesToContext(int displayId, @NonNull Configuration newConfig) {
+    private void applyConfigChangesToContext(@NonNull DisplayAreaInfo displayAreaInfo) {
+        final int displayId = displayAreaInfo.displayId;
+        final Display display = mContext.getSystemService(DisplayManager.class)
+                .getDisplay(displayId);
+        if (display == null) {
+            throw new UnsupportedOperationException("The display #" + displayId + " is invalid."
+                    + "displayAreaInfo:" + displayAreaInfo);
+        }
         DisplayAreaContext daContext = mDisplayAreaContexts.get(displayId);
         if (daContext == null) {
-            daContext = new DisplayAreaContext(mContext, displayId);
+            daContext = new DisplayAreaContext(mContext, display);
             mDisplayAreaContexts.put(displayId, daContext);
         }
-        daContext.updateConfigurationChanges(newConfig);
+        daContext.updateConfigurationChanges(displayAreaInfo.configuration);
     }
 
     /**
@@ -228,10 +231,8 @@
         private final IBinder mToken = new Binder();
         private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
 
-        public DisplayAreaContext(@NonNull Context context, int displayId) {
+        public DisplayAreaContext(@NonNull Context context, @NonNull Display display) {
             super(null);
-            final Display display = context.getSystemService(DisplayManager.class)
-                    .getDisplay(displayId);
             attachBaseContext(context.createTokenContext(mToken, display));
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index cb04bd7..fcb53cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,6 +31,7 @@
 import android.app.TaskInfo;
 import android.content.Context;
 import android.content.LocusId;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.util.ArrayMap;
@@ -307,9 +308,10 @@
     }
 
     @Override
-    public void removeStartingWindow(int taskId) {
+    public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
         if (mStartingSurface != null) {
-            mStartingSurface.removeStartingWindow(taskId);
+            mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 46884fe..7d65a08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -83,6 +83,7 @@
     private boolean mIsInitialized;
     private Listener mListener;
     private Executor mListenerExecutor;
+    private Rect mObscuredTouchRect;
 
     private final Rect mTmpRect = new Rect();
     private final Rect mTmpRootRect = new Rect();
@@ -161,6 +162,15 @@
     }
 
     /**
+     * Indicates a region of the view that is not touchable.
+     *
+     * @param obscuredRect the obscured region of the view.
+     */
+    public void setObscuredTouchRect(Rect obscuredRect) {
+        mObscuredTouchRect = obscuredRect;
+    }
+
+    /**
      * Call when view position or size has changed. Do not call when animating.
      */
     public void onLocationChanged() {
@@ -384,6 +394,10 @@
         mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
                 mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
         inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+
+        if (mObscuredTouchRect != null) {
+            inoutInfo.touchableRegion.union(mObscuredTouchRect);
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 562b32b..b6d408a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,6 +23,7 @@
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 import android.window.WindowContainerToken;
@@ -88,7 +89,8 @@
         ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
                 task1.taskId, task2.taskId, this);
 
-        if (!task1.isResizeable || !task2.isResizeable) {
+        if ((!task1.isResizeable || !task2.isResizeable)
+                && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
             ProtoLog.e(WM_SHELL_TASK_ORG,
                     "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
                     task1.isResizeable, task2.isResizeable);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 64a44ca..16ede73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -41,7 +41,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Region;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.Log;
@@ -1046,6 +1045,7 @@
         }
     };
 
+    // TODO: Create ManageMenuView and move setup / animations there
     private void setUpManageMenu() {
         if (mManageMenu != null) {
             removeView(mManageMenu);
@@ -2146,50 +2146,6 @@
     }
 
     /**
-     * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a
-     * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV).
-     * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided
-     * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to
-     * the special nature of ActivityView, it does not respect the standard
-     * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for
-     * this purpose.
-     *
-     * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation
-     * properties for performance reasons. This means that the default implementation of this method
-     * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in
-     * it not receiving any touch events. This was previously addressed by returning false in the
-     * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any
-     * touch handlers in the stack or its child views.
-     *
-     * To support touch handlers, we're overriding this method to leave the ActivityView's touchable
-     * region alone. The only touchable part of the stack that can ever overlap the AV is a
-     * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually
-     * updating the touchable region to allow users to grab a bubble while it completes its ~50ms
-     * animation back to the bubble row.
-     *
-     * NOTE: Any future additions to the stack that obscure the ActivityView region will need their
-     * bounds subtracted here in order to receive touch events.
-     */
-    @Override
-    public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
-        // If the notification shade is expanded, or the manage menu is open, or we are showing
-        // manage bubbles user education, we shouldn't let the ActivityView steal any touch events
-        // from any location.
-        if (!mIsExpanded
-                || mShowingManage
-                || (mManageEduView != null
-                    && mManageEduView.getVisibility() == VISIBLE)) {
-            touchableRegion.setEmpty();
-        }
-    }
-
-    /**
-     * If you're here because you're not receiving touch events on a view that is a descendant of
-     * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the
-     * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView
-     * consumes all touch events within its bounds, even for views like the BubbleStackView that are
-     * above it. It ignores typical view touch handling methods like this one and
-     * dispatchTouchEvent.
      */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -2539,6 +2495,11 @@
         }
 
         mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+        if (mExpandedBubble.getExpandedView().getTaskView() != null) {
+            mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
+                    ? new Rect(0, 0, getWidth(), getHeight())
+                    : null);
+        }
 
         final boolean isLtr =
                 getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
index 7ce9014..57a9dd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
@@ -143,14 +143,14 @@
     @ImeAnimationFlags
     public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
             boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
-        mHiddenTop = hiddenTop;
-        mShownTop = shownTop;
-        mTargetShown = imeShouldShow;
         if (!isDividerVisible()) {
             return 0;
         }
-        final boolean splitIsVisible = !getView().isHidden();
+        mHiddenTop = hiddenTop;
+        mShownTop = shownTop;
+        mTargetShown = imeShouldShow;
         mSecondaryHasFocus = getSecondaryHasFocus(displayId);
+        final boolean splitIsVisible = !getView().isHidden();
         final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
                 && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
                 && !mSplits.mSplitScreenController.isMinimized();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
index eea5c08..d06064a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
@@ -30,6 +30,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -91,9 +92,11 @@
                         // is nothing behind it.
                         ((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
                                 && triggerTask.parentTaskId == mListener.mPrimary.taskId)
-                        // if a non-resizable is launched, we also need to leave split-screen.
+                        // if a non-resizable is launched when it is not supported in multi window,
+                        // we also need to leave split-screen.
                         || ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
-                                && !triggerTask.isResizeable);
+                                && !triggerTask.isResizeable
+                                && !ActivityTaskManager.supportsNonResizableMultiWindow());
                 // In both cases, dismiss the primary
                 if (shouldDismiss) {
                     WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 82468ad..5a2ef56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,6 +46,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.BooleanSupplier;
 
 /**
  * Proxy to simplify calls into window manager/activity manager
@@ -208,11 +209,17 @@
             return false;
         }
         ActivityManager.RunningTaskInfo topHomeTask = null;
+        // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
+        // because the value can be changed at runtime.
+        final BooleanSupplier supportsNonResizableMultiWindow =
+                createSupportsNonResizableMultiWindowSupplier();
         for (int i = rootTasks.size() - 1; i >= 0; --i) {
             final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
-            // Only move resizeable task to split secondary. However, we have an exception
-            // for non-resizable home because we will minimize to show it.
-            if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
+            // Check whether to move resizeable task to split secondary.
+            // Also, we have an exception for non-resizable home because we will minimize to show
+            // it.
+            if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
+                    && !supportsNonResizableMultiWindow.getAsBoolean()) {
                 continue;
             }
             // Only move fullscreen tasks to split secondary.
@@ -357,6 +364,21 @@
         outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
     }
 
+    /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
+    private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
+        return new BooleanSupplier() {
+            private Boolean mSupportsNonResizableMultiWindow;
+            @Override
+            public boolean getAsBoolean() {
+                if (mSupportsNonResizableMultiWindow == null) {
+                    mSupportsNonResizableMultiWindow =
+                            ActivityTaskManager.supportsNonResizableMultiWindow();
+                }
+                return mSupportsNonResizableMultiWindow;
+            }
+        };
+    }
+
     /**
      * Utility to apply a sync transaction serially with other sync transactions.
      *
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9ec7c0d..36dc4e4 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
@@ -41,6 +41,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.PictureInPictureParams;
@@ -423,12 +424,16 @@
 
         if (mInSwipePipToHomeTransition) {
             final Rect destinationBounds = mPipBoundsState.getBounds();
+            final SurfaceControl.Transaction tx =
+                    mSurfaceControlTransactionFactory.getTransaction();
+            mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds);
+            mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds);
             // animation is finished in the Launcher and here we directly apply the final touch.
             applyEnterPipSyncTransaction(destinationBounds, () -> {
                 // ensure menu's settled in its final bounds first
                 finishResizeForMenu(destinationBounds);
                 sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
-            });
+            }, tx);
             mInSwipePipToHomeTransition = false;
             return;
         }
@@ -490,16 +495,20 @@
             // mState is set right after the animation is kicked off to block any resize
             // requests such as offsetPip that may have been called prior to the transition.
             mState = State.ENTERING_PIP;
-        });
+        }, null /* boundsChangeTransaction */);
     }
 
-    private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
+    private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable,
+            @Nullable SurfaceControl.Transaction boundsChangeTransaction) {
         // PiP menu is attached late in the process here to avoid any artifacts on the leash
         // caused by addShellRoot when in gesture navigation mode.
         mPipMenuController.attach(mLeash);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
         wct.setBounds(mToken, destinationBounds);
+        if (boundsChangeTransaction != null) {
+            wct.setBoundsChangeTransaction(mToken, boundsChangeTransaction);
+        }
         wct.scheduleFinishEnterPip(mToken, destinationBounds);
         mSyncTransactionQueue.queue(wct);
         if (runnable != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
new file mode 100644
index 0000000..5bc2afd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.startingsurface;
+
+import static android.view.View.GONE;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateYAnimation;
+import android.window.SplashScreenView;
+
+import com.android.wm.shell.common.TransactionPool;
+
+/**
+ * Default animation for exiting the splash screen window.
+ * @hide
+ */
+public class SplashScreenExitAnimation implements Animator.AnimatorListener {
+    private static final boolean DEBUG_EXIT_ANIMATION = false;
+    private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+    private static final String TAG = StartingSurfaceDrawer.TAG;
+
+    private static final Interpolator ICON_EXIT_INTERPOLATOR = new PathInterpolator(1f, 0f, 1f, 1f);
+    private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
+
+    private static final int EXTRA_REVEAL_DELAY = 133;
+    private final Matrix mTmpTransform = new Matrix();
+    private final float[] mTmpFloat9 = new float[9];
+    private SurfaceControl mFirstWindowSurface;
+    private final Rect mFirstWindowFrame = new Rect();
+    private final SplashScreenView mSplashScreenView;
+    private final int mMainWindowShiftLength;
+    private final int mIconShiftLength;
+    private final int mAppDuration;
+    private final int mIconDuration;
+    private final TransactionPool mTransactionPool;
+
+    private ValueAnimator mMainAnimator;
+    private Animation mShiftUpAnimation;
+    private AnimationSet mIconAnimationSet;
+    private Runnable mFinishCallback;
+
+    SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame,
+            int appDuration, int iconDuration, int mainWindowShiftLength, int iconShiftLength,
+            TransactionPool pool, Runnable handleFinish) {
+        mSplashScreenView = view;
+        mFirstWindowSurface = leash;
+        if (frame != null) {
+            mFirstWindowFrame.set(frame);
+        }
+        mAppDuration = appDuration;
+        mIconDuration = iconDuration;
+        mMainWindowShiftLength = mainWindowShiftLength;
+        mIconShiftLength = iconShiftLength;
+        mFinishCallback = handleFinish;
+        mTransactionPool = pool;
+    }
+
+    void prepareAnimations() {
+        prepareRevealAnimation();
+        prepareShiftAnimation();
+    }
+
+    void startAnimations() {
+        if (mIconAnimationSet != null) {
+            mIconAnimationSet.start();
+        }
+        if (mMainAnimator != null) {
+            mMainAnimator.start();
+        }
+        if (mShiftUpAnimation != null) {
+            mShiftUpAnimation.start();
+        }
+    }
+
+    // reveal splash screen, shift up main window
+    private void prepareRevealAnimation() {
+        // splash screen
+        mMainAnimator = ValueAnimator.ofFloat(0f, 1f);
+        mMainAnimator.setDuration(mAppDuration);
+        mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR);
+        mMainAnimator.addListener(this);
+
+        final int startDelay = mIconDuration + EXTRA_REVEAL_DELAY;
+        final float transparentRatio = 0.95f;
+        final int globalHeight = mSplashScreenView.getHeight();
+        final int verticalCircleCenter = 0;
+        final int finalVerticalLength = globalHeight - verticalCircleCenter;
+        final int halfWidth = mSplashScreenView.getWidth() / 2;
+        final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
+                Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
+        final RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(
+                mSplashScreenView, mMainAnimator);
+        radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
+        radialVanishAnimation.setRadius(0/* initRadius */, endRadius);
+        final int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT, Color.WHITE};
+        final float[] stops = {0f, transparentRatio, 1f};
+        radialVanishAnimation.setRadialPaintParam(colors, stops);
+        radialVanishAnimation.setReady();
+        mMainAnimator.setStartDelay(startDelay);
+
+        if (mFirstWindowSurface != null) {
+            // shift up main window
+            View occludeHoleView = new View(mSplashScreenView.getContext());
+            if (DEBUG_EXIT_ANIMATION_BLEND) {
+                occludeHoleView.setBackgroundColor(Color.BLUE);
+            } else {
+                occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor());
+            }
+            final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
+                    WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
+            mSplashScreenView.addView(occludeHoleView, params);
+
+            mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength);
+            mShiftUpAnimation.setDuration(mAppDuration);
+            mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR);
+            mShiftUpAnimation.setStartOffset(startDelay);
+
+            occludeHoleView.setAnimation(mShiftUpAnimation);
+        }
+    }
+
+    // shift down icon and branding view
+    private void prepareShiftAnimation() {
+        final View iconView = mSplashScreenView.getIconView();
+        if (iconView == null) {
+            return;
+        }
+        if (mIconShiftLength > 0) {
+            mIconAnimationSet = new AnimationSet(true /* shareInterpolator */);
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "first exit animation, shift length: " + mIconShiftLength);
+            }
+            mIconAnimationSet.addAnimation(new TranslateYAnimation(0, mIconShiftLength));
+            mIconAnimationSet.addAnimation(new AlphaAnimation(1, 0));
+            mIconAnimationSet.setAnimationListener(new Animation.AnimationListener() {
+                @Override
+                public void onAnimationStart(Animation animation) {
+
+                }
+
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    if (DEBUG_EXIT_ANIMATION) {
+                        Slog.v(TAG, "first exit animation finished");
+                    }
+                    iconView.post(() -> iconView.setVisibility(GONE));
+                }
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {
+                    // ignore
+                }
+            });
+            mIconAnimationSet.setDuration(mIconDuration);
+            mIconAnimationSet.setInterpolator(ICON_EXIT_INTERPOLATOR);
+            iconView.setAnimation(mIconAnimationSet);
+            final View brandingView = mSplashScreenView.getBrandingView();
+            if (brandingView != null) {
+                brandingView.setAnimation(mIconAnimationSet);
+            }
+        }
+    }
+
+    private static class RadialVanishAnimation extends View {
+        private SplashScreenView mView;
+        private int mInitRadius;
+        private int mFinishRadius;
+        private boolean mReady;
+
+        private final Point mCircleCenter = new Point();
+        private final Matrix mVanishMatrix = new Matrix();
+        private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        RadialVanishAnimation(SplashScreenView target, ValueAnimator animator) {
+            super(target.getContext());
+            mView = target;
+            animator.addUpdateListener((animation) -> {
+                if (mVanishPaint.getShader() == null) {
+                    return;
+                }
+                final float value = (float) animation.getAnimatedValue();
+                final float scale = (mFinishRadius - mInitRadius) * value + mInitRadius;
+                mVanishMatrix.setScale(scale, scale);
+                mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
+                mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
+                mView.postInvalidate();
+            });
+            mView.addView(this);
+        }
+
+        void setRadius(int initRadius, int finishRadius) {
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
+                        + " final " + finishRadius);
+            }
+            mInitRadius = initRadius;
+            mFinishRadius = finishRadius;
+        }
+
+        void setCircleCenter(int x, int y) {
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
+            }
+            mCircleCenter.set(x, y);
+        }
+
+        void setRadialPaintParam(int[] colors, float[] stops) {
+            // setup gradient shader
+            final RadialGradient rShader =
+                    new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
+            mVanishPaint.setShader(rShader);
+            if (!DEBUG_EXIT_ANIMATION_BLEND) {
+                mVanishPaint.setBlendMode(BlendMode.MODULATE);
+            }
+        }
+
+        void setReady() {
+            mReady = true;
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            if (mReady) {
+                canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
+            }
+        }
+    }
+
+    private final class ShiftUpAnimation extends TranslateYAnimation {
+        ShiftUpAnimation(float fromYDelta, float toYDelta) {
+            super(fromYDelta, toYDelta);
+        }
+
+        @Override
+        protected void applyTransformation(float interpolatedTime, Transformation t) {
+            super.applyTransformation(interpolatedTime, t);
+
+            if (mFirstWindowSurface == null) {
+                return;
+            }
+            mTmpTransform.set(t.getMatrix());
+            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+            mTmpTransform.postTranslate(mFirstWindowFrame.left,
+                    mFirstWindowFrame.top + mMainWindowShiftLength);
+            tx.setMatrix(mFirstWindowSurface, mTmpTransform, mTmpFloat9);
+            // TODO set the vsyncId to ensure the transaction doesn't get applied too early.
+            //  Additionally, do you want to have this synchronized with your view animations?
+            //  If so, you'll need to use SyncRtSurfaceTransactionApplier
+            tx.apply();
+            mTransactionPool.release(tx);
+        }
+    }
+
+    private void reset() {
+        if (DEBUG_EXIT_ANIMATION) {
+            Slog.v(TAG, "vanish animation finished");
+        }
+        mSplashScreenView.post(() -> {
+            mSplashScreenView.setVisibility(GONE);
+            if (mFinishCallback != null) {
+                mFinishCallback.run();
+                mFinishCallback = null;
+            }
+        });
+        if (mFirstWindowSurface != null) {
+            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+            tx.setWindowCrop(mFirstWindowSurface, null);
+            tx.apply();
+            mFirstWindowSurface.release();
+            mFirstWindowSurface = null;
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+        // ignore
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        reset();
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+        reset();
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+        // ignore
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 2973b50..3f9c271 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -31,12 +31,14 @@
 import android.graphics.drawable.LayerDrawable;
 import android.os.Build;
 import android.util.Slog;
+import android.view.SurfaceControl;
 import android.window.SplashScreenView;
 
 import com.android.internal.R;
 import com.android.internal.graphics.palette.Palette;
 import com.android.internal.graphics.palette.Quantizer;
 import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.wm.shell.common.TransactionPool;
 
 import java.util.List;
 
@@ -56,15 +58,25 @@
     // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
     private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
     private final Context mContext;
-    private final int mMaxIconAnimationDuration;
+    private final int mMaxAnimatableIconDuration;
 
     private int mIconSize;
     private int mBrandingImageWidth;
     private int mBrandingImageHeight;
+    private final int mAppRevealDuration;
+    private final int mIconExitDuration;
+    private int mMainWindowShiftLength;
+    private int mIconNormalExitDistance;
+    private int mIconEarlyExitDistance;
+    private final TransactionPool mTransactionPool;
 
-    SplashscreenContentDrawer(Context context, int maxIconAnimationDuration) {
+    SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
+            int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
         mContext = context;
-        mMaxIconAnimationDuration = maxIconAnimationDuration;
+        mMaxAnimatableIconDuration = maxAnimatableIconDuration;
+        mAppRevealDuration = appRevealAnimDuration;
+        mIconExitDuration = iconExitAnimDuration;
+        mTransactionPool = pool;
     }
 
     private void updateDensity() {
@@ -74,6 +86,12 @@
                 com.android.wm.shell.R.dimen.starting_surface_brand_image_width);
         mBrandingImageHeight = mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.starting_surface_brand_image_height);
+        mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
+        mIconNormalExitDistance = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_normal_exit_icon_distance);
+        mIconEarlyExitDistance = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_early_exit_icon_distance);
     }
 
     private int getSystemBGColor() {
@@ -119,7 +137,7 @@
         if (attrs.mReplaceIcon != null) {
             iconDrawable = attrs.mReplaceIcon;
             animationDuration = Math.max(0,
-                    Math.min(attrs.mAnimationDuration, mMaxIconAnimationDuration));
+                    Math.min(attrs.mAnimationDuration, mMaxAnimatableIconDuration));
         } else {
             iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
                     : context.getPackageManager().getDefaultActivityIcon();
@@ -439,8 +457,8 @@
         }
 
         /**
-         * For ColorDrawable only.
-         * There will be only one color so don't spend too much resource for it.
+         * For ColorDrawable only. There will be only one color so don't spend too much resource for
+         * it.
          */
         private static class SingleColorTester implements ColorTester {
             private final ColorDrawable mColorDrawable;
@@ -472,9 +490,8 @@
         }
 
         /**
-         * For any other Drawable except ColorDrawable.
-         * This will use the Palette API to check the color information and use a quantizer to
-         * filter out transparent colors when needed.
+         * For any other Drawable except ColorDrawable. This will use the Palette API to check the
+         * color information and use a quantizer to filter out transparent colors when needed.
          */
         private static class ComplexDrawableTester implements ColorTester {
             private static final int MAX_BITMAP_SIZE = 40;
@@ -593,4 +610,17 @@
             }
         }
     }
+
+    /**
+     * Create and play the default exit animation for splash screen view.
+     */
+    void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
+            Rect frame, boolean isEarlyExit, Runnable finishCallback) {
+        final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash,
+                frame, mAppRevealDuration, mIconExitDuration, mMainWindowShiftLength,
+                isEarlyExit ? mIconEarlyExitDistance : mIconNormalExitDistance, mTransactionPool,
+                finishCallback);
+        animation.prepareAnimations();
+        animation.startAnimations();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
index a594a9f..f258286 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
@@ -16,7 +16,9 @@
 
 package com.android.wm.shell.startingsurface;
 
+import android.graphics.Rect;
 import android.os.IBinder;
+import android.view.SurfaceControl;
 import android.window.StartingWindowInfo;
 
 import java.util.function.BiConsumer;
@@ -31,7 +33,8 @@
     /**
      * Called when the content of a task is ready to show, starting window can be removed.
      */
-    void removeStartingWindow(int taskId);
+    void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation);
     /**
      * Called when the Task wants to copy the splash screen.
      * @param taskId
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 2d1d65b..50d8098 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -29,14 +29,19 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
+import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.view.Choreographer;
 import android.view.Display;
+import android.view.SurfaceControl;
 import android.view.View;
-import android.view.Window;
 import android.view.WindowManager;
 import android.window.SplashScreenView;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
@@ -46,6 +51,7 @@
 import com.android.internal.R;
 import com.android.internal.policy.PhoneWindow;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
 
 import java.util.function.Consumer;
 
@@ -62,23 +68,23 @@
     private final DisplayManager mDisplayManager;
     private final ShellExecutor mSplashScreenExecutor;
     private final SplashscreenContentDrawer mSplashscreenContentDrawer;
-    protected Choreographer mChoreographer;
 
     // TODO(b/131727939) remove this when clearing ActivityRecord
     private static final int REMOVE_WHEN_TIMEOUT = 2000;
 
-    public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor) {
+    public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
+            TransactionPool pool) {
         mContext = context;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mSplashScreenExecutor = splashScreenExecutor;
-        final int maxIconAnimDuration = context.getResources().getInteger(
+        final int maxAnimatableIconDuration = context.getResources().getInteger(
                 com.android.wm.shell.R.integer.max_starting_window_intro_icon_anim_duration);
-        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, maxIconAnimDuration);
-        mSplashScreenExecutor.execute(this::initChoreographer);
-    }
-
-    protected void initChoreographer() {
-        mChoreographer = Choreographer.getInstance();
+        final int iconExitAnimDuration = context.getResources().getInteger(
+                com.android.wm.shell.R.integer.starting_window_icon_exit_anim_duration);
+        final int appRevealAnimDuration = context.getResources().getInteger(
+                com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
+        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext,
+                maxAnimatableIconDuration, iconExitAnimDuration, appRevealAnimDuration, pool);
     }
 
     private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -144,11 +150,12 @@
         context = displayContext;
         if (theme != context.getThemeResId() || labelRes != 0) {
             try {
-                context = context.createPackageContext(
-                        activityInfo.packageName, CONTEXT_RESTRICTED);
+                context = context.createPackageContextAsUser(activityInfo.packageName,
+                        CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
                 context.setTheme(theme);
             } catch (PackageManager.NameNotFoundException e) {
-                // Ignore
+                Slog.w(TAG, "Failed creating package context with package name "
+                        + activityInfo.packageName + " for user " + taskInfo.userId, e);
             }
         }
 
@@ -195,6 +202,7 @@
         }
 
         final PhoneWindow win = new PhoneWindow(context);
+        win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
         win.setIsStartingWindow(true);
 
         CharSequence label = context.getResources().getText(labelRes, null);
@@ -211,7 +219,7 @@
         // the keyguard is being hidden. This is okay because starting windows never show
         // secret information.
         // TODO(b/113840485): Occluded may not only happen on default display
-        if (displayId == DEFAULT_DISPLAY) {
+        if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
             windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
         }
 
@@ -247,6 +255,7 @@
         // Setting as trusted overlay to let touches pass through. This is safe because this
         // window is controlled by the system.
         params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+        params.format = PixelFormat.RGBA_8888;
 
         final Resources res = context.getResources();
         final boolean supportsScreen = res != null && (res.getCompatibilityInfo() != null
@@ -257,98 +266,25 @@
 
         params.setTitle("Splash Screen " + activityInfo.packageName);
 
-        // TODO(b/173975965) If the target activity doesn't request FLAG_HARDWARE_ACCELERATED, we
-        // cannot replace the content view after first view was drawn, sounds like an issue.
-        new AddSplashScreenViewRunnable(taskInfo.taskId, win, context, appToken, params, iconRes,
-                        splashscreenContentResId[0], enableHardAccelerated).run();
-    }
-
-    private class AddSplashScreenViewRunnable implements Runnable {
-        private final int mTaskId;
-        private final Window mWin;
-        private final IBinder mAppToken;
-        private final WindowManager.LayoutParams mLayoutParams;
-        private final Context mContext;
-        private final int mIconRes;
-        private final int mSplashscreenContentResId;
-        private final boolean mReplaceSplashScreenView;
-        private int mSequence;
-
-        AddSplashScreenViewRunnable(int taskId, Window window, Context context,
-                IBinder appToken, WindowManager.LayoutParams params, int iconRes,
-                int splashscreenContentResId, boolean replaceSplashScreenView) {
-            mTaskId = taskId;
-            mWin = window;
-            mAppToken = appToken;
-            mContext = context;
-            mLayoutParams = params;
-            mIconRes = iconRes;
-            mSplashscreenContentResId = splashscreenContentResId;
-            mReplaceSplashScreenView = replaceSplashScreenView;
-        }
-
-        private void createInitialView() {
-            View tempView = new View(mContext);
-            mWin.setContentView(tempView);
-            mSequence++;
-            final View view = mWin.getDecorView();
+        // TODO(b/173975965) tracking performance
+        final int taskId = taskInfo.taskId;
+        SplashScreenView sView = null;
+        try {
+            sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes,
+                            splashscreenContentResId[0]);
+            final View view = win.getDecorView();
             final WindowManager wm = mContext.getSystemService(WindowManager.class);
-            if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) {
-                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, this, null);
+            if (postAddWindow(taskId, appToken, view, wm, params)) {
+                win.setContentView(sView);
+                sView.cacheRootWindow(win);
             }
-        }
-
-        private SplashScreenView replaceRealView() {
-            final SplashScreenView sView =
-                    mSplashscreenContentDrawer.makeSplashScreenContentView(mContext,
-                            mIconRes, mSplashscreenContentResId);
-            mWin.setContentView(sView);
-            sView.cacheRootWindow(mWin);
-            return sView;
-        }
-
-        private SplashScreenView initiateOnce() {
-            final SplashScreenView sView =
-                    mSplashscreenContentDrawer.makeSplashScreenContentView(mContext, mIconRes,
-                            mSplashscreenContentResId);
-            final View view = mWin.getDecorView();
-            final WindowManager wm = mContext.getSystemService(WindowManager.class);
-            if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) {
-                mWin.setContentView(sView);
-                sView.cacheRootWindow(mWin);
-            }
-            return sView;
-        }
-
-        @Override
-        public void run() {
-            SplashScreenView view = null;
-            boolean setRecord = false;
-            try {
-                if (mReplaceSplashScreenView) {
-                    // Tricky way to make animation start faster... create the real content after
-                    // first window drawn. The first empty window won't been see because wm will
-                    // still need to wait for transition ready.
-                    if (mSequence == 0) {
-                        createInitialView();
-                    } else if (mSequence == 1) {
-                        setRecord = true;
-                        view = replaceRealView();
-                    }
-                } else {
-                    setRecord = true;
-                    view = initiateOnce();
-                }
-            } catch (RuntimeException e) {
-                // don't crash if something else bad happens, for example a
-                // failure loading resources because we are loading from an app
-                // on external storage that has been unmounted.
-                Slog.w(TAG, " failed creating starting window", e);
-            } finally {
-                if (setRecord) {
-                    setSplashScreenRecord(mTaskId, view);
-                }
-            }
+        } catch (RuntimeException e) {
+            // don't crash if something else bad happens, for example a
+            // failure loading resources because we are loading from an app
+            // on external storage that has been unmounted.
+            Slog.w(TAG, " failed creating starting window", e);
+        } finally {
+            setSplashScreenRecord(taskId, sView);
         }
     }
 
@@ -359,8 +295,10 @@
             TaskSnapshot snapshot) {
         final int taskId = startingWindowInfo.taskInfo.taskId;
         final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
-                snapshot, mSplashScreenExecutor, () -> removeWindowSynced(taskId));
-        mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+                snapshot, mSplashScreenExecutor,
+                () -> removeWindowNoAnimate(taskId));
+        mSplashScreenExecutor.executeDelayed(() -> removeWindowNoAnimate(taskId),
+                REMOVE_WHEN_TIMEOUT);
         final StartingWindowRecord tView =
                 new StartingWindowRecord(null/* decorView */, surface);
         mStartingWindowRecords.put(taskId, tView);
@@ -369,11 +307,12 @@
     /**
      * Called when the content of a task is ready to show, starting window can be removed.
      */
-    public void removeStartingWindow(int taskId) {
+    public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
             Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
         }
-        removeWindowSynced(taskId);
+        removeWindowSynced(taskId, leash, frame, playRevealAnimation);
     }
 
     /**
@@ -383,13 +322,6 @@
     public void copySplashScreenView(int taskId) {
         final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
         SplashScreenViewParcelable parcelable;
-        if (preView != null) {
-            if (preView.isWaitForContent()) {
-                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
-                        () -> copySplashScreenView(taskId), null);
-                return;
-            }
-        }
         if (preView != null && preView.mContentView != null
                 && preView.mContentView.isCopyable()) {
             parcelable = new SplashScreenViewParcelable(preView.mContentView);
@@ -413,12 +345,6 @@
             Slog.w(TAG, appToken + " already running, starting window not displayed. "
                     + e.getMessage());
             shouldSaveView = false;
-        } catch (RuntimeException e) {
-            // don't crash if something else bad happens, for example a
-            // failure loading resources because we are loading from an app
-            // on external storage that has been unmounted.
-            Slog.w(TAG, appToken + " failed creating starting window", e);
-            shouldSaveView = false;
         } finally {
             if (view != null && view.getParent() == null) {
                 Slog.w(TAG, "view not successfully added to wm, removing view");
@@ -427,9 +353,9 @@
             }
         }
         if (shouldSaveView) {
-            removeWindowSynced(taskId);
-            mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId),
-                    REMOVE_WHEN_TIMEOUT);
+            removeWindowNoAnimate(taskId);
+            mSplashScreenExecutor.executeDelayed(
+                    () -> removeWindowNoAnimate(taskId), REMOVE_WHEN_TIMEOUT);
             saveSplashScreenRecord(taskId, view);
         }
         return shouldSaveView;
@@ -449,24 +375,30 @@
         }
     }
 
-    protected void removeWindowSynced(int taskId) {
+    private void removeWindowNoAnimate(int taskId) {
+        removeWindowSynced(taskId, null, null, false);
+    }
+
+    protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
         final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
         if (record != null) {
-            if (record.isWaitForContent()) {
-                if (DEBUG_SPLASH_SCREEN) {
-                    Slog.v(TAG, "splash screen window haven't been draw yet");
-                }
-                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
-                        () -> removeWindowSynced(taskId), null);
-                return;
-            }
             if (record.mDecorView != null) {
                 if (DEBUG_SPLASH_SCREEN) {
                     Slog.v(TAG, "Removing splash screen window for task: " + taskId);
                 }
-                final WindowManager wm = record.mDecorView.getContext()
-                        .getSystemService(WindowManager.class);
-                wm.removeView(record.mDecorView);
+                if (record.mContentView != null) {
+                    final HandleExitFinish exitFinish = new HandleExitFinish(record.mDecorView);
+                    if (leash != null || playRevealAnimation) {
+                        mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
+                                leash, frame, record.isEarlyExit(), exitFinish);
+                        mSplashScreenExecutor.executeDelayed(exitFinish, REMOVE_WHEN_TIMEOUT);
+                    } else {
+                        // the SplashScreenView has been copied to client, skip default exit
+                        // animation
+                        exitFinish.run();
+                    }
+                }
             }
             if (record.mTaskSnapshotWindow != null) {
                 if (DEBUG_TASK_SNAPSHOT) {
@@ -478,6 +410,26 @@
         }
     }
 
+    private static class HandleExitFinish implements Runnable {
+        private View mDecorView;
+
+        HandleExitFinish(View decorView) {
+            mDecorView = decorView;
+        }
+
+        @Override
+        public void run() {
+            if (mDecorView == null) {
+                return;
+            }
+            final WindowManager wm = mDecorView.getContext().getSystemService(WindowManager.class);
+            if (wm != null) {
+                wm.removeView(mDecorView);
+            }
+            mDecorView = null;
+        }
+    }
+
     private void getWindowResFromContext(Context ctx, Consumer<TypedArray> consumer) {
         final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
         consumer.accept(a);
@@ -488,10 +440,12 @@
      * Record the view or surface for a starting window.
      */
     private static class StartingWindowRecord {
+        private static final long EARLY_START_MINIMUM_TIME_MS = 250;
         private final View mDecorView;
         private final TaskSnapshotWindow mTaskSnapshotWindow;
         private SplashScreenView mContentView;
         private boolean mSetSplashScreen;
+        private long mContentCreateTime;
 
         StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
             mDecorView = decorView;
@@ -503,11 +457,12 @@
                 return;
             }
             mContentView = splashScreenView;
+            mContentCreateTime = SystemClock.uptimeMillis();
             mSetSplashScreen = true;
         }
 
-        private boolean isWaitForContent() {
-            return mDecorView != null && !mSetSplashScreen;
+        boolean isEarlyExit() {
+            return SystemClock.uptimeMillis() - mContentCreateTime < EARLY_START_MINIMUM_TIME_MS;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index 5eb7071..a694e52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -28,14 +28,17 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.SurfaceControl;
 import android.window.StartingWindowInfo;
 import android.window.TaskOrganizer;
 import android.window.TaskSnapshot;
 
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
 
 import java.util.function.BiConsumer;
 
@@ -67,8 +70,16 @@
     private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
     private final ShellExecutor mSplashScreenExecutor;
 
+    // For Car Launcher
     public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) {
-        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor);
+        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor,
+                new TransactionPool());
+        mSplashScreenExecutor = splashScreenExecutor;
+    }
+
+    public StartingWindowController(Context context, ShellExecutor splashScreenExecutor,
+            TransactionPool pool) {
+        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool);
         mSplashScreenExecutor = splashScreenExecutor;
     }
 
@@ -112,7 +123,8 @@
                         + " allowTaskSnapshot " + allowTaskSnapshot
                         + " activityCreated " + activityCreated);
             }
-            if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+            if ((newTask || !processRunning || (taskSwitch && !activityCreated))
+                    && windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
                 return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
             }
             if (taskSwitch && allowTaskSnapshot) {
@@ -198,8 +210,9 @@
     /**
      * Called when the content of a task is ready to show, starting window can be removed.
      */
-    void removeStartingWindow(int taskId) {
-        mStartingSurfaceDrawer.removeStartingWindow(taskId);
+    void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
+        mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
     }
 
     private class StartingSurfaceImpl implements StartingSurface {
@@ -211,9 +224,11 @@
         }
 
         @Override
-        public void removeStartingWindow(int taskId) {
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
             mSplashScreenExecutor.execute(() ->
-                    StartingWindowController.this.removeStartingWindow(taskId));
+                    StartingWindowController.this.removeStartingWindow(taskId, leash, frame,
+                            playRevealAnimation));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index f06d57c..ad4ccc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -11,6 +11,8 @@
         <option name="force-skip-system-props" value="true" />
         <!-- set WM tracing verbose level to all -->
         <option name="run-command" value="cmd window tracing level all" />
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame" />
         <!-- restart launcher to activate TAPL -->
         <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
     </target_preparer>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 90e7137..98ce274 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,6 +18,8 @@
 
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -25,6 +27,8 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.wm.shell.flicker.appPairsDividerIsInvisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -32,11 +36,10 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test AppPairs launch.
- * To run this test: `atest WMShellFlickerTests:AppPairsTest`
- */
-/**
- * Test cold launch app from launcher.
+ * Test cold launch app from launcher. When the device doesn't support non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair
+ * non-resizable apps.
+ *
  * To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
  */
 @RequiresDevice
@@ -46,6 +49,7 @@
 class AppPairsTestCannotPairNonResizeableApps(
     testSpec: FlickerTestParameter
 ) : AppPairsTransition(testSpec) {
+    var prevSupportNonResizableInMultiWindow = 0
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
@@ -59,6 +63,32 @@
             }
         }
 
+    @Before
+    fun setup() {
+        prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+            Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+        if (prevSupportNonResizableInMultiWindow == 1) {
+            // Not support non-resizable in multi window
+            Settings.Global.putInt(context.contentResolver,
+                    Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+        }
+    }
+
+    @After
+    fun teardown() {
+        Settings.Global.putInt(context.contentResolver,
+                Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+                prevSupportNonResizableInMultiWindow)
+    }
+
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index dc51b4f..63e9a78 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -56,6 +56,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
new file mode 100644
index 0000000..1e3595c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launch app from launcher. When the device supports non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair
+ * non-resizable apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTestSupportPairNonResizeableApps(
+    testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+    var prevSupportNonResizableInMultiWindow = 0
+
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            transitions {
+                nonResizeableApp?.launchViaIntent(wmHelper)
+                // TODO pair apps through normal UX flow
+                executeShellCommand(
+                        composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
+                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+            }
+        }
+
+    @Before
+    fun setup() {
+        prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+                Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+        if (prevSupportNonResizableInMultiWindow == 0) {
+            // Support non-resizable in multi window
+            Settings.Global.putInt(context.contentResolver,
+                    Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+        }
+    }
+
+    @After
+    fun teardown() {
+        Settings.Global.putInt(context.contentResolver,
+                Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+                prevSupportNonResizableInMultiWindow)
+    }
+
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+    @Presubmit
+    @Test
+    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+    @Presubmit
+    @Test
+    fun bothAppWindowVisible() {
+        val nonResizeableApp = nonResizeableApp
+        require(nonResizeableApp != null) {
+            "Non resizeable app not initialized"
+        }
+        testSpec.assertWmEnd {
+            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(primaryApp.defaultWindowName)
+        }
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                    repetitions = AppPairsHelper.TEST_REPETITIONS)
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 5bb9b2f..234dda4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -61,6 +61,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 91e080f..134d00b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -17,22 +17,27 @@
 package com.android.wm.shell.flicker.apppairs
 
 import android.app.Instrumentation
+import android.content.Context
 import android.platform.test.annotations.Presubmit
 import android.system.helpers.ActivityHelper
 import android.util.Log
+import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -42,6 +47,7 @@
 
 abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    protected val context: Context = instrumentation.context
     protected val isRotated = testSpec.config.startRotation.isRotated()
     protected val activityHelper = ActivityHelper.getInstance()
     protected val appPairsHelper = AppPairsHelper(instrumentation,
@@ -134,17 +140,39 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsAlwaysVisible() {
+        testSpec.navBarLayerIsAlwaysVisible()
+    }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsAlwaysVisible() {
+        testSpec.statusBarLayerIsAlwaysVisible()
+    }
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsAlwaysVisible() {
+        testSpec.navBarWindowIsAlwaysVisible()
+    }
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsAlwaysVisible() {
+        testSpec.statusBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
+            testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+            testSpec.config.endRotation)
+    }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 5f003ba..d341bb1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -27,8 +27,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.wm.shell.flicker.appPairsDividerIsVisible
 import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
 import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
@@ -75,16 +73,6 @@
     @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
 
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
     @FlakyTest(bugId = 172776659)
     @Test
     fun appPairsPrimaryBoundsIsVisible() =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index d479208..3bf0296 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -27,16 +27,13 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.appPairsDividerIsVisible
 import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
 import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -67,20 +64,10 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
-    @Presubmit
-    @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
-        Surface.ROTATION_0, testSpec.config.endRotation)
-
-    @Presubmit
-    @Test
     override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 75c33c6..0333227 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -23,13 +23,7 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -48,7 +42,6 @@
     testSpec: FlickerTestParameter
 ) : PipTransition(testSpec) {
     private val testApp = FixedAppHelper(instrumentation)
-    private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true) {
@@ -84,14 +77,6 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun showBothAppLayersThenHidePip() {
         testSpec.assertLayers {
             isVisible(testApp.defaultWindowName)
@@ -118,14 +103,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 2f08db1..4847c98 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -24,14 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.startRotation
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -57,15 +50,7 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun pipWindowBecomesVisible() {
+    fun pipAppWindowAlwaysVisible() {
         testSpec.assertWm {
             this.showsAppWindow(pipApp.defaultWindowName)
         }
@@ -73,34 +58,37 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() =
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
     fun pipLayerBecomesVisible() {
         testSpec.assertLayers {
             this.isVisible(pipApp.launcherName)
         }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Postsubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun pipWindowBecomesVisible() {
+        testSpec.assertWm {
+            invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
+                .then()
+                .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+        }
+    }
 
     @FlakyTest(bugId = 140855415)
     @Test
-    fun navBarLayerRotatesAndScales() =
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
 
     @FlakyTest(bugId = 140855415)
     @Test
-    fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest(bugId = 140855415)
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+    @FlakyTest(bugId = 140855415)
+    @Test
+    override fun noUncoveredRegions() = super.noUncoveredRegions()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 9011f1a..ba88ee5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -18,16 +18,13 @@
 
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -83,6 +80,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun pipAppWindowIsAlwaysOnTop() {
@@ -109,14 +114,6 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun pipAppLayerHidesTestApp() {
         testSpec.assertLayersStart {
             coversExactly(startingBounds, pipApp.defaultWindowName)
@@ -132,14 +129,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index 3e33176..eae7e97 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -24,14 +24,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.Test
 import org.junit.runners.Parameterized
 
@@ -52,22 +45,6 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     open fun pipWindowBecomesInvisible() {
         testSpec.assertWm {
             this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -86,21 +63,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    open fun statusBarLayerRotatesScales() =
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    open fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    open fun navBarLayerRotatesAndScales() =
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
     @FlakyTest(bugId = 151179149)
     @Test
     open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
index afaf33a..c7a1c9a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -51,40 +51,40 @@
             }
         }
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun noUncoveredRegions() = super.noUncoveredRegions()
 
-    @Postsubmit
+    @Presubmit
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 97afc65..bf148bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -24,17 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.ImeAppHelper
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.removeAllTasksButHome
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
 import org.junit.FixMethodOrder
@@ -55,7 +49,6 @@
 class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val imeApp = ImeAppHelper(instrumentation)
     private val testApp = FixedAppHelper(instrumentation)
-    private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
@@ -85,7 +78,7 @@
             }
         }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun pipWindowInsideDisplayBounds() {
         testSpec.assertWm {
@@ -93,7 +86,7 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
@@ -103,15 +96,15 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun pipLayerInsideDisplayBounds() {
         testSpec.assertLayers {
@@ -119,7 +112,7 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun bothAppLayersVisible() {
         testSpec.assertLayersEnd {
@@ -128,13 +121,13 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
 
     companion object {
         const val TEST_REPETITIONS = 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index 4c95da2..d011419 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -24,8 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -59,11 +57,11 @@
 
     @Postsubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
     @Postsubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
     @Postsubmit
     @Test
@@ -71,6 +69,14 @@
 
     @Postsubmit
     @Test
+    fun pipLayerInsideDisplay() {
+        testSpec.assertLayersStart {
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
+        }
+    }
+
+    @Postsubmit
+    @Test
     fun pipWindowMovesUp() = testSpec.assertWmEnd {
         val initialState = this.trace?.first()?.wmState
             ?: error("Trace should not be empty")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index df835d2..49a1055 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -29,10 +29,6 @@
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
@@ -77,34 +73,18 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     @FlakyTest(bugId = 140855415)
     @Test
-    fun navBarLayerRotatesAndScales() =
+    override fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
     @FlakyTest(bugId = 140855415)
     @Test
-    fun statusBarLayerRotatesScales() =
+    override fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 1bb1d28..945a20b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -26,14 +26,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -68,22 +61,6 @@
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun appReplacesPipWindow() {
         testSpec.assertWm {
             this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -94,11 +71,6 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() =
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
     fun appReplacesPipLayer() {
         testSpec.assertLayers {
             this.isVisible(PIP_WINDOW_TITLE)
@@ -107,15 +79,13 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
-    fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() =
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    fun testAppCoversFullScreen() {
+        testSpec.assertLayersStart {
+            coversExactly(displayBounds, pipApp.defaultWindowName)
+        }
+    }
 
     @FlakyTest(bugId = 151179149)
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b0a9afe..7dc7e7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -18,27 +18,37 @@
 
 import android.app.Instrumentation
 import android.content.Intent
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.PipAppHelper
 import com.android.wm.shell.flicker.removeAllTasksButHome
 import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Test
 
 abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     protected val isRotated = testSpec.config.startRotation.isRotated()
     protected val pipApp = PipAppHelper(instrumentation)
+    protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
     protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
     protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-
     // Helper class to process test actions by broadcast.
     protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
         private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -148,4 +158,35 @@
             extraSpec(this, configuration)
         }
     }
+
+    @Presubmit
+    @Test
+    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+    @Presubmit
+    @Test
+    open fun noUncoveredRegions() =
+        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 7916ce5..67e1768 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -25,10 +26,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
@@ -83,6 +80,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun pipWindowInsideDisplay() {
@@ -101,20 +106,18 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
             coversAtMost(startingBounds, pipApp.defaultWindowName)
         }
     }
 
+    @Postsubmit
+    @Test
+    fun pipAlwaysVisible() = testSpec.assertWm {
+        this.showsAppWindow(pipApp.windowName)
+    }
+
     @Presubmit
     @Test
     fun pipAppLayerCoversFullScreen() {
@@ -123,14 +126,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index a531ef5..207db9e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package unittest.src.com.android.wm.shell.startingsurface;
+package com.android.wm.shell.startingsurface;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -33,11 +33,12 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.testing.TestableContext;
-import android.view.Choreographer;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
@@ -49,7 +50,7 @@
 
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
+import com.android.wm.shell.common.TransactionPool;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -68,7 +69,7 @@
     @Mock
     private WindowManager mMockWindowManager;
     @Mock
-    private static Choreographer sFakeChoreographer;
+    private TransactionPool mTransactionPool;
 
     TestStartingSurfaceDrawer mStartingSurfaceDrawer;
 
@@ -76,13 +77,9 @@
         int mAddWindowForTask = 0;
         int mViewThemeResId;
 
-        TestStartingSurfaceDrawer(Context context, ShellExecutor executor) {
-            super(context, executor);
-        }
-
-        @Override
-        protected void initChoreographer() {
-            mChoreographer = sFakeChoreographer;
+        TestStartingSurfaceDrawer(Context context, ShellExecutor animExecutor,
+                TransactionPool pool) {
+            super(context, animExecutor, pool);
         }
 
         @Override
@@ -95,7 +92,8 @@
         }
 
         @Override
-        protected void removeWindowSynced(int taskId) {
+        protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
             // listen for removeView
             if (mAddWindowForTask == taskId) {
                 mAddWindowForTask = 0;
@@ -123,7 +121,8 @@
         doNothing().when(mMockWindowManager).addView(any(), any());
 
         mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context,
-                new HandlerExecutor(new Handler(Looper.getMainLooper()))));
+                new HandlerExecutor(new Handler(Looper.getMainLooper())),
+                mTransactionPool));
     }
 
     @Test
@@ -137,9 +136,9 @@
         verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
         assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
 
-        mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId);
+        mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false);
         waitHandlerIdle(mainLoop);
-        verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId));
+        verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId), any(), any(), eq(false));
         assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 27e5f51..b908df2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package unittest.src.com.android.wm.shell.startingsurface;
+package com.android.wm.shell.startingsurface;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -48,7 +48,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index aba0f1b..63b831d 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -97,8 +97,8 @@
                 "libincfs",
                 "libutils",
                 "libz",
-                "libziparchive",
             ],
+            static_libs: ["libziparchive_for_incfs"],
             static: {
                 enabled: false,
             },
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index c0ef7be..7e45f95 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -226,8 +226,6 @@
 }
 
 void AssetManager2::DumpToLog() const {
-  base::ScopedLogSeverity _log(base::INFO);
-
   LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
 
   std::string list;
@@ -1721,7 +1719,6 @@
 }
 
 void Theme::Dump() const {
-  base::ScopedLogSeverity _log(base::INFO);
   LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
 
   for (int p = 0; p < packages_.size(); p++) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d663c52..607ef72 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -388,11 +388,10 @@
         "liblog",
         "libminikin",
         "libz",
-        "libziparchive",
         "libjpeg",
     ],
 
-    static_libs: ["libnativehelper_lazy"],
+    static_libs: ["libnativehelper_lazy", "libziparchive_for_incfs"],
 
     target: {
         android: {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9793300..800c580 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,6 +576,7 @@
 
     if (requireSwap) {
         if (mExpectSurfaceStats) {
+            reportMetricsWithPresentTime();
             std::lock_guard lock(mLast4FrameInfosMutex);
             std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
             next.first = mCurrentFrameInfo;
@@ -656,8 +657,6 @@
         }
     }
 
-    instance->reportMetricsWithPresentTime();
-
     if (frameInfo != nullptr) {
         if (gpuCompleteTime == -1) {
             gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e93824d..0112686 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -336,6 +336,7 @@
     GET_DEV_PROC(ResetCommandBuffer);
     GET_DEV_PROC(ResetFences);
     GET_DEV_PROC(WaitForFences);
+    GET_DEV_PROC(FrameBoundaryANDROID);
 }
 
 void VulkanManager::initialize() {
@@ -516,6 +517,25 @@
     if (semaphore != VK_NULL_HANDLE) {
         if (submitted == GrSemaphoresSubmitted::kYes) {
             mSwapSemaphore = semaphore;
+            if (mFrameBoundaryANDROID) {
+                // retrieve VkImage used as render target
+                VkImage image = VK_NULL_HANDLE;
+                GrBackendRenderTarget backendRenderTarget =
+                        surface->getBackendRenderTarget(SkSurface::kFlushRead_BackendHandleAccess);
+                if (backendRenderTarget.isValid()) {
+                    GrVkImageInfo info;
+                    if (backendRenderTarget.getVkImageInfo(&info)) {
+                        image = info.fImage;
+                    } else {
+                        ALOGE("Frame boundary: backend is not vulkan");
+                    }
+                } else {
+                    ALOGE("Frame boundary: invalid backend render target");
+                }
+                // frameBoundaryANDROID needs to know about mSwapSemaphore, but
+                // it won't wait on it.
+                mFrameBoundaryANDROID(mDevice, mSwapSemaphore, image);
+            }
         } else {
             destroy_semaphore(mDestroySemaphoreContext);
             mDestroySemaphoreContext = nullptr;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 0912369..7b5fe19 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -31,6 +31,21 @@
 #include <vk/GrVkExtensions.h>
 #include <vulkan/vulkan.h>
 
+// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
+// (https://github.com/google/agi) to enable profiling of apps rendering via
+// HWUI. This extension is not defined in Khronos, hence the need to declare it
+// manually here. There's a superseding extension (VK_EXT_frame_boundary) being
+// discussed in Khronos, but in the meantime we use the bespoke
+// VK_ANDROID_frame_boundary. This is a device extension that is implemented by
+// AGI's Vulkan capture layer, such that it is only supported by devices when
+// AGI is doing a capture of the app.
+//
+// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has
+// landed in the spec.
+typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore,
+                                                    VkImage image);
+#define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary"
+
 #include "Frame.h"
 #include "IRenderPipeline.h"
 #include "VulkanSurface.h"
@@ -160,6 +175,7 @@
     VkPtr<PFN_vkDestroyFence> mDestroyFence;
     VkPtr<PFN_vkWaitForFences> mWaitForFences;
     VkPtr<PFN_vkResetFences> mResetFences;
+    VkPtr<PFN_vkFrameBoundaryANDROID> mFrameBoundaryANDROID;
 
     VkInstance mInstance = VK_NULL_HANDLE;
     VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 09c6a4f..a8f2d9a 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -52,18 +52,10 @@
         return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
     }
 
-    static bool isBlendedShader(const SkShader* shader) {
-        if (shader == nullptr) {
-            return false;
-        }
-        return !shader->isOpaque();
-    }
+    static bool isBlendedShader(const SkShader* shader) { return shader && !shader->isOpaque(); }
 
     static bool isBlendedColorFilter(const SkColorFilter* filter) {
-        if (filter == nullptr) {
-            return false;
-        }
-        return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+        return filter && !filter->isAlphaUnchanged();
     }
 
     static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
diff --git a/media/Android.bp b/media/Android.bp
index 9268b22..a66236e 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -27,6 +27,9 @@
 aidl_interface {
     name: "media_permission-aidl",
     unstable: true,
+    host_supported: true,
+    vendor_available: true,
+    double_loadable: true,
     local_include_dir: "aidl",
     srcs: [
         "aidl/android/media/permission/Identity.aidl",
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
index 361497d..3638904 100644
--- a/media/aidl/android/media/permission/Identity.aidl
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -22,11 +22,11 @@
  */
 parcelable Identity {
     /** Linux user ID. */
-    int uid;
+    int uid = -1;
     /** Linux process ID. */
-    int pid;
+    int pid = -1;
     /** Package name. If null, the first package owned by the given uid will be assumed. */
-    @nullable String packageName;
+    @nullable @utf8InCpp String packageName;
     /** Attribution tag. Mostly used for diagnostic purposes. */
-    @nullable String attributionTag;
+    @nullable @utf8InCpp String attributionTag;
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1cef0922..e8e2631 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6729,7 +6729,7 @@
      */
     public Map<Integer, Boolean> getSurroundFormats() {
         Map<Integer, Boolean> surroundFormats = new HashMap<>();
-        int status = AudioSystem.getSurroundFormats(surroundFormats, false);
+        int status = AudioSystem.getSurroundFormats(surroundFormats);
         if (status != AudioManager.SUCCESS) {
             // fail and bail!
             Log.e(TAG, "getSurroundFormats failed:" + status);
@@ -6762,20 +6762,17 @@
     /**
      * @hide
      * Returns all surround formats that are reported by the connected HDMI device.
-     * The keys are not affected by calling setSurroundFormatEnabled(), and the values
-     * are not affected by calling setSurroundFormatEnabled() when in AUTO mode.
-     * This information can used to show the AUTO setting for SurroundSound.
+     * The return values are not affected by calling setSurroundFormatEnabled.
      *
-     * @return a map where the key is a surround format and
-     * the value indicates the surround format is enabled or not
+     * @return a list of surround formats
      */
-    public Map<Integer, Boolean> getReportedSurroundFormats() {
-        Map<Integer, Boolean> reportedSurroundFormats = new HashMap<>();
-        int status = AudioSystem.getSurroundFormats(reportedSurroundFormats, true);
+    public ArrayList<Integer> getReportedSurroundFormats() {
+        ArrayList<Integer> reportedSurroundFormats = new ArrayList<>();
+        int status = AudioSystem.getReportedSurroundFormats(reportedSurroundFormats);
         if (status != AudioManager.SUCCESS) {
             // fail and bail!
             Log.e(TAG, "getReportedSurroundFormats failed:" + status);
-            return new HashMap<Integer, Boolean>(); // Always return a map.
+            return new ArrayList<Integer>(); // Always return a list.
         }
         return reportedSurroundFormats;
     }
@@ -7087,26 +7084,22 @@
      * <pre class="prettyprint">
      * // Get an AudioManager instance
      * AudioManager audioManager = Context.getSystemService(AudioManager.class);
-     * try {
-     *     AudioDeviceInfo speakerDevice = null;
-     *     List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
-     *     for (AudioDeviceInfo device : devices) {
-     *         if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
-     *             speakerDevice = device;
-     *             break;
-     *         }
+     * AudioDeviceInfo speakerDevice = null;
+     * List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
+     * for (AudioDeviceInfo device : devices) {
+     *     if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+     *         speakerDevice = device;
+     *         break;
      *     }
-     *     if (speakerDevice != null) {
-     *         // Turn speakerphone ON.
-     *         boolean result = audioManager.setCommunicationDevice(speakerDevice);
-     *         if (!result) {
-     *             // Handle error.
-     *         }
-     *         // Turn speakerphone OFF.
-     *         audioManager.clearCommunicationDevice();
+     * }
+     * if (speakerDevice != null) {
+     *     // Turn speakerphone ON.
+     *     boolean result = audioManager.setCommunicationDevice(speakerDevice);
+     *     if (!result) {
+     *         // Handle error.
      *     }
-     * } catch (IllegalArgumentException e) {
-     *     // Handle exception.
+     *     // Turn speakerphone OFF.
+     *     audioManager.clearCommunicationDevice();
      * }
      * </pre>
      * @param device the requested audio device.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index bf04b66..d7112d6 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.permission.PermissionUtil.myIdentity;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
@@ -26,9 +28,11 @@
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.media.MediaRecorder.Source;
 import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioPolicy;
+import android.media.permission.Identity;
 import android.media.projection.MediaProjection;
 import android.os.Binder;
 import android.os.Build;
@@ -54,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -352,6 +357,32 @@
     @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
     public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
             int sessionId) throws IllegalArgumentException {
+        this(attributes, format, bufferSizeInBytes, sessionId, ActivityThread.currentApplication());
+    }
+
+    /**
+     * @hide
+     * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
+     * @param attributes a non-null {@link AudioAttributes} instance. Use
+     *     {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio
+     *     source for this instance.
+     * @param format a non-null {@link AudioFormat} instance describing the format of the data
+     *     that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for
+     *     configuring the audio format parameters such as encoding, channel mask and sample rate.
+     * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
+     *   to during the recording. New audio data can be read from this buffer in smaller chunks
+     *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
+     *   required buffer size for the successful creation of an AudioRecord instance. Using values
+     *   smaller than getMinBufferSize() will result in an initialization failure.
+     * @param sessionId ID of audio session the AudioRecord must be attached to, or
+     *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
+     *   time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before
+     *   construction.
+     * @param context An optional context to pull an attribution tag from.
+     * @throws IllegalArgumentException
+     */
+    private AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
+            int sessionId, @Nullable Context context) throws IllegalArgumentException {
         mRecordingState = RECORDSTATE_STOPPED;
 
         if (attributes == null) {
@@ -414,15 +445,21 @@
 
         audioBuffSizeCheck(bufferSizeInBytes);
 
+        Identity identity = myIdentity(context);
+        if (identity.packageName == null) {
+            // Command line utility
+            identity.packageName = "uid:" + Binder.getCallingUid();
+        }
+
         int[] sampleRate = new int[] {mSampleRate};
         int[] session = new int[1];
         session[0] = sessionId;
         //TODO: update native initialization when information about hardware init failure
         //      due to capture device already open is available.
-        int initResult = native_setup( new WeakReference<AudioRecord>(this),
+        int initResult = native_setup(new WeakReference<AudioRecord>(this),
                 mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
                 mAudioFormat, mNativeBufferSizeInBytes,
-                session, getCurrentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
+                session, identity, 0 /*nativeRecordInJavaObj*/);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing native AudioRecord object.");
             return; // with mState == STATE_UNINITIALIZED
@@ -434,15 +471,6 @@
         mState = STATE_INITIALIZED;
     }
 
-    private String getCurrentOpPackageName() {
-        String opPackageName = ActivityThread.currentOpPackageName();
-        if (opPackageName != null) {
-            return opPackageName;
-        }
-        // Command line utility
-        return "uid:" + Binder.getCallingUid();
-    }
-
     /**
      * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
      * the AudioRecordRoutingProxy subclass.
@@ -492,7 +520,7 @@
                     0 /*mAudioFormat*/,
                     0 /*mNativeBufferSizeInBytes*/,
                     session,
-                    ActivityThread.currentOpPackageName(),
+                    myIdentity(null),
                     nativeRecordInJavaObj);
             if (initResult != SUCCESS) {
                 loge("Error code "+initResult+" when initializing native AudioRecord object.");
@@ -548,6 +576,7 @@
         private AudioPlaybackCaptureConfiguration mAudioPlaybackCaptureConfiguration;
         private AudioAttributes mAttributes;
         private AudioFormat mFormat;
+        private Context mContext;
         private int mBufferSizeInBytes;
         private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
         private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
@@ -583,6 +612,18 @@
         }
 
         /**
+         * Sets the context the record belongs to.
+         * @param context a non-null {@link Context} instance
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setContext(@NonNull Context context) {
+            Objects.requireNonNull(context);
+            // keep reference, we only copy the data when building
+            mContext = context;
+            return this;
+        }
+
+        /**
          * @hide
          * To be only used by system components. Allows specifying non-public capture presets
          * @param attributes a non-null {@link AudioAttributes} instance that contains the capture
@@ -793,7 +834,7 @@
                             * mFormat.getBytesPerSample(mFormat.getEncoding());
                 }
                 final AudioRecord record = new AudioRecord(
-                        mAttributes, mFormat, mBufferSizeInBytes, mSessionId);
+                        mAttributes, mFormat, mBufferSizeInBytes, mSessionId, mContext);
                 if (record.getState() == STATE_UNINITIALIZED) {
                     // release is not necessary
                     throw new UnsupportedOperationException("Cannot create AudioRecord");
@@ -2035,15 +2076,32 @@
     // Native methods called from the Java side
     //--------------------
 
-    @UnsupportedAppUsage
-    private native final int native_setup(Object audiorecord_this,
+    /**
+     * @deprecated Use native_setup that takes an Identity object
+     * @return
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
+            publicAlternatives = "{@code AudioRecord.Builder}")
+    @Deprecated
+    private int native_setup(Object audiorecordThis,
             Object /*AudioAttributes*/ attributes,
             int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
             int buffSizeInBytes, int[] sessionId, String opPackageName,
-            long nativeRecordInJavaObj);
+            long nativeRecordInJavaObj) {
+        Identity identity = myIdentity(null);
+        identity.packageName = opPackageName;
+
+        return native_setup(audiorecordThis, attributes, sampleRate, channelMask, channelIndexMask,
+                audioFormat, buffSizeInBytes, sessionId, identity, nativeRecordInJavaObj);
+    }
+
+    private native int native_setup(Object audiorecordThis,
+            Object /*AudioAttributes*/ attributes,
+            int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+            int buffSizeInBytes, int[] sessionId, Identity identity, long nativeRecordInJavaObj);
 
     // TODO remove: implementation calls directly into implementation of native_release()
-    private native final void native_finalize();
+    private native void native_finalize();
 
     /**
      * @hide
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f1f6cb1..8134d6f 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1699,8 +1699,10 @@
     public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
 
     /** @hide */
-    public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats,
-                                                boolean reported);
+    public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats);
+
+    /** @hide */
+    public static native int getReportedSurroundFormats(ArrayList<Integer> surroundFormats);
 
     /**
      * @hide
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 548b415..ae64c02 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -2937,9 +2936,7 @@
      * @return a {@link PlaybackComponent} associated with the session,
      * or {@code null} if the session is closed or does not exist.
      * @see PlaybackComponent
-     * @hide
      */
-    @TestApi
     @Nullable
     public PlaybackComponent getPlaybackComponent(@NonNull byte[] sessionId) {
         if (sessionId == null) {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 9176dae..3de78bb 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.BIND_IMS_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.permission.PermissionUtil.myIdentity;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -34,6 +35,7 @@
 import android.graphics.SurfaceTexture;
 import android.media.SubtitleController.Anchor;
 import android.media.SubtitleTrack.RenderingWidget;
+import android.media.permission.Identity;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -53,6 +55,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
@@ -684,11 +687,14 @@
         mTimeProvider = new TimeProvider(this);
         mOpenSubtitleSources = new Vector<InputStream>();
 
+        Identity identity = myIdentity(null);
+        // set the package name to empty if it was null
+        identity.packageName = TextUtils.emptyIfNull(identity.packageName);
+
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
          */
-        native_setup(new WeakReference<MediaPlayer>(this),
-                getCurrentOpPackageName());
+        native_setup(new WeakReference<MediaPlayer>(this), identity);
 
         baseRegisterPlayer(sessionId);
     }
@@ -2471,7 +2477,7 @@
     private native final int native_setMetadataFilter(Parcel request);
 
     private static native final void native_init();
-    private native void native_setup(Object mediaplayerThis, @NonNull String opPackageName);
+    private native void native_setup(Object mediaplayerThis, @NonNull Identity identity);
     private native final void native_finalize();
 
     /**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 49a4cc6..87e1e5b 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.permission.PermissionUtil.myIdentity;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
@@ -25,7 +27,9 @@
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.hardware.Camera;
+import android.media.permission.Identity;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -48,6 +52,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -127,9 +132,21 @@
 
     /**
      * Default constructor.
+     *
+     * @deprecated Use {@link #MediaRecorder(Context)} instead
      */
+    @Deprecated
     public MediaRecorder() {
+        this(ActivityThread.currentApplication());
+    }
 
+    /**
+     * Default constructor.
+     *
+     * @param context Context the recorder belongs to
+     */
+    public MediaRecorder(@NonNull Context context) {
+        Objects.requireNonNull(context);
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
             mEventHandler = new EventHandler(this, looper);
@@ -140,12 +157,11 @@
         }
 
         mChannelCount = 1;
-        String packageName = ActivityThread.currentPackageName();
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
          */
-        native_setup(new WeakReference<MediaRecorder>(this), packageName,
-                ActivityThread.currentOpPackageName());
+        native_setup(new WeakReference<MediaRecorder>(this),
+                ActivityThread.currentPackageName(), myIdentity(context));
     }
 
     /**
@@ -1740,12 +1756,22 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static native final void native_init();
 
-    @UnsupportedAppUsage
-    private native final void native_setup(Object mediarecorder_this,
-            String clientName, String opPackageName) throws IllegalStateException;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
+            publicAlternatives = "{@link MediaRecorder}")
+    private void native_setup(Object mediarecorderThis,
+            String clientName, String opPackageName) throws IllegalStateException {
+        Identity identity = myIdentity(null);
+        identity.packageName = opPackageName;
+
+        native_setup(mediarecorderThis, clientName, identity);
+    }
+
+    private native void native_setup(Object mediarecorderThis,
+            String clientName, Identity identity)
+            throws IllegalStateException;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private native final void native_finalize();
+    private native void native_finalize();
 
     @UnsupportedAppUsage
     private native void setParameter(String nameValuePair);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index dc9c58e..5f44b62 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -21,6 +21,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -60,6 +61,7 @@
 public final class MediaRouter2 {
     private static final String TAG = "MR2";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final Object sSystemRouterLock = new Object();
     private static final Object sRouterLock = new Object();
 
     // The maximum time for the old routing controller available after transfer.
@@ -67,8 +69,8 @@
     // The manager request ID representing that no manager is involved.
     private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE;
 
-    @GuardedBy("sRouterLock")
-    private static Map<String, MediaRouter2> sMediaRouter2Map = new ArrayMap<>();
+    @GuardedBy("sSystemRouterLock")
+    private static Map<String, MediaRouter2> sSystemMediaRouter2Map = new ArrayMap<>();
     private static MediaRouter2Manager sManager;
 
     @GuardedBy("sRouterLock")
@@ -76,6 +78,7 @@
 
     private final Context mContext;
     private final IMediaRouterService mMediaRouterService;
+    private final Object mLock = new Object();
 
     private final CopyOnWriteArrayList<RouteCallbackRecord> mRouteCallbackRecords =
             new CopyOnWriteArrayList<>();
@@ -87,28 +90,31 @@
     private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests =
             new CopyOnWriteArrayList<>();
 
+    // TODO: Specify the fields that are only used (or not used) by system media router.
     private final String mClientPackageName;
+    private final ManagerCallback mManagerCallback;
+
     private final String mPackageName;
 
-    @GuardedBy("sRouterLock")
+    @GuardedBy("mLock")
     final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>();
 
     final RoutingController mSystemController;
 
-    @GuardedBy("sRouterLock")
+    @GuardedBy("mLock")
     private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
 
     // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
-    @GuardedBy("sRouterLock")
+    @GuardedBy("mLock")
     MediaRouter2Stub mStub;
 
-    @GuardedBy("sRouterLock")
+    @GuardedBy("mLock")
     private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>();
 
     private final AtomicInteger mNextRequestId = new AtomicInteger(1);
 
     final Handler mHandler;
-    @GuardedBy("sRouterLock")
+    @GuardedBy("mLock")
     private boolean mShouldUpdateRoutes = true;
     private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
     private volatile OnGetControllerHintsListener mOnGetControllerHintsListener;
@@ -128,13 +134,34 @@
     }
 
     /**
-     * Gets an instance of the media router which controls the app's media routing.
+     * Gets an instance of the system media router which controls the app's media routing.
      * Returns {@code null} if the given package name is invalid.
+     * There are several things to note when using the media routers created with this method.
+     * <p>
+     * First of all, the discovery preference passed to {@link #registerRouteCallback}
+     * will have no effect. The callback will be called accordingly with the client app's
+     * discovery preference. Therefore, it is recommended to pass
+     * {@link RouteDiscoveryPreference#EMPTY} there.
+     * <p>
+     * Also, do not keep/compare the instances of the {@link RoutingController}, since they are
+     * always newly created with the latest session information whenever below methods are called:
+     * <ul>
+     * <li> {@link #getControllers()} </li>
+     * <li> {@link #getController(String)}} </li>
+     * <li> {@link TransferCallback#onTransfer(RoutingController, RoutingController)} </li>
+     * <li> {@link TransferCallback#onStop(RoutingController)} </li>
+     * <li> {@link ControllerCallback#onControllerUpdated(RoutingController)} </li>
+     * </ul>
+     * Therefore, in order to track the current routing status, keep the controller's ID instead,
+     * and use {@link #getController(String)} and {@link #getSystemController()} for
+     * getting controllers.
+     * <p>
+     * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}.
      *
      * @param clientPackageName the package name of the app to control
      * @hide
      */
-    //@SystemApi
+    @SystemApi
     @Nullable
     public static MediaRouter2 getInstance(@NonNull Context context,
             @NonNull String clientPackageName) {
@@ -149,20 +176,50 @@
             return null;
         }
 
-        synchronized (sRouterLock) {
-            MediaRouter2 instance = sMediaRouter2Map.get(clientPackageName);
+        synchronized (sSystemRouterLock) {
+            MediaRouter2 instance = sSystemMediaRouter2Map.get(clientPackageName);
             if (instance == null) {
                 // TODO: Add permission check here using MODIFY_AUDIO_ROUTING.
                 if (sManager == null) {
                     sManager = MediaRouter2Manager.getInstance(context.getApplicationContext());
                 }
                 instance = new MediaRouter2(context, clientPackageName);
-                sMediaRouter2Map.put(clientPackageName, instance);
+                sSystemMediaRouter2Map.put(clientPackageName, instance);
+                // TODO: Remove router instance once it is not needed.
+                instance.registerManagerCallbackForSystemRouter();
             }
             return instance;
         }
     }
 
+    /**
+     * Starts scanning remote routes.
+     * Note that calling start/stopScan is applied to all system routers in the same process.
+     *
+     * @see #stopScan()
+     * @hide
+     */
+    @SystemApi
+    public void startScan() {
+        if (isSystemRouter()) {
+            sManager.startScan();
+        }
+    }
+
+    /**
+     * Stops scanning remote routes to reduce resource consumption.
+     * Note that calling start/stopScan is applied to all system routers in the same process.
+     *
+     * @see #startScan()
+     * @hide
+     */
+    @SystemApi
+    public void stopScan() {
+        if (isSystemRouter()) {
+            sManager.stopScan();
+        }
+    }
+
     private MediaRouter2(Context appContext) {
         mContext = appContext;
         mMediaRouterService = IMediaRouterService.Stub.asInterface(
@@ -192,16 +249,21 @@
         }
         mSystemController = new SystemRoutingController(currentSystemSessionInfo);
 
+        // Only used by system MediaRouter2.
         mClientPackageName = null;
+        mManagerCallback = null;
     }
 
     private MediaRouter2(Context context, String clientPackageName) {
-        mClientPackageName = clientPackageName;
         mContext = context;
-        mMediaRouterService = null;
-        mPackageName = null;
+        mClientPackageName = clientPackageName;
+        mManagerCallback = new ManagerCallback();
         mHandler = new Handler(Looper.getMainLooper());
-        mSystemController = null;
+        mSystemController = new SystemRoutingController(sManager.getSystemRoutingSession());
+        mMediaRouterService = null; // TODO: Make this non-null and check permission.
+
+        // Only used by non-system MediaRouter2.
+        mPackageName = null;
     }
 
     /**
@@ -220,13 +282,13 @@
     }
 
     /**
-     * Gets the target package name of the app which this media router controls.
-     * This is only non-null when the router instance is created with the target package name.
+     * Gets the client package name of the app which this media router controls.
+     * This is only non-null when the router instance is created with the client package name.
      *
      * @see #getInstance(Context, String)
      * @hide
      */
-    //@SystemApi
+    @SystemApi
     @Nullable
     public String getClientPackageName() {
         return mClientPackageName;
@@ -245,6 +307,9 @@
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(routeCallback, "callback must not be null");
         Objects.requireNonNull(preference, "preference must not be null");
+        if (isSystemRouter()) {
+            preference = RouteDiscoveryPreference.EMPTY;
+        }
 
         RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, preference);
 
@@ -253,7 +318,11 @@
         // is happening but it's okay because either this or the other registration should be done.
         mRouteCallbackRecords.addIfAbsent(record);
 
-        synchronized (sRouterLock) {
+        if (isSystemRouter()) {
+            return;
+        }
+
+        synchronized (mLock) {
             if (mStub == null) {
                 MediaRouter2Stub stub = new MediaRouter2Stub();
                 try {
@@ -289,7 +358,11 @@
             return;
         }
 
-        synchronized (sRouterLock) {
+        if (isSystemRouter()) {
+            return;
+        }
+
+        synchronized (mLock) {
             if (mStub == null) {
                 return;
             }
@@ -326,22 +399,34 @@
     }
 
     /**
+     * Gets the list of all discovered routes.
+     * This list includes the routes that are not related to the client app.
+     * <p>
+     * This will return an empty list for non-system media routers.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public List<MediaRoute2Info> getAllRoutes() {
+        if (isSystemRouter()) {
+            return sManager.getAllRoutes();
+        }
+        return Collections.emptyList();
+    }
+
+    /**
      * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
      * known to the media router.
      * <p>
      * Please note that the list can be changed before callbacks are invoked.
      * </p>
-     *
      * @return the list of routes that contains at least one of the route features in discovery
      * preferences registered by the application
      */
     @NonNull
     public List<MediaRoute2Info> getRoutes() {
-        if (mClientPackageName != null) {
-            return sManager.getAvailableRoutes(mClientPackageName);
-        }
-
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             if (mShouldUpdateRoutes) {
                 mShouldUpdateRoutes = false;
 
@@ -434,6 +519,9 @@
      *                 {@code null} for unset.
      */
     public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) {
+        if (isSystemRouter()) {
+            return;
+        }
         mOnGetControllerHintsListener = listener;
     }
 
@@ -449,7 +537,7 @@
      * @see TransferCallback#onTransferFailure
      */
     public void transferTo(@NonNull MediaRoute2Info route) {
-        if (mClientPackageName != null) {
+        if (isSystemRouter()) {
             sManager.selectRoute(mClientPackageName, route);
             return;
         }
@@ -464,7 +552,7 @@
      * controls the media routing, this method is a no-op.
      */
     public void stop() {
-        if (mClientPackageName != null) {
+        if (isSystemRouter()) {
             List<RoutingSessionInfo> sessionInfos = sManager.getRoutingSessions(mClientPackageName);
             RoutingSessionInfo sessionToRelease = sessionInfos.get(sessionInfos.size() - 1);
             sManager.releaseSession(sessionToRelease);
@@ -479,9 +567,9 @@
      * @param route the route you want to transfer the media to.
      * @hide
      */
-    //@SystemApi
+    @SystemApi
     public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
-        if (mClientPackageName != null) {
+        if (isSystemRouter()) {
             sManager.transfer(controller.getRoutingSessionInfo(), route);
             return;
         }
@@ -490,7 +578,7 @@
         Objects.requireNonNull(route, "route must not be null");
 
         boolean routeFound;
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             // TODO: Check thread-safety
             routeFound = mRoutes.containsKey(route.getId());
         }
@@ -526,7 +614,7 @@
         }
 
         MediaRouter2Stub stub;
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             stub = mStub;
         }
         if (stub != null) {
@@ -566,6 +654,23 @@
     }
 
     /**
+     * Gets a {@link RoutingController} whose ID is equal to the given ID.
+     * Returns {@code null} if there is no matching controller.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public RoutingController getController(@NonNull String id) {
+        Objects.requireNonNull(id, "id must not be null");
+        for (RoutingController controller : getControllers()) {
+            if (TextUtils.equals(id, controller.getId())) {
+                return controller;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Gets the list of currently active {@link RoutingController routing controllers} on which
      * media can be played.
      * <p>
@@ -574,17 +679,27 @@
      */
     @NonNull
     public List<RoutingController> getControllers() {
-        // TODO: Do not create the controller instances every time,
-        //       Instead, update the list using the sessions' ID and session related callbacks.
-        if (mClientPackageName != null) {
-            return sManager.getRoutingSessions(mClientPackageName).stream()
-                    .map(info -> new RoutingController(info))
-                    .collect(Collectors.toList());
+        List<RoutingController> result = new ArrayList<>();
+
+        if (isSystemRouter()) {
+            // Unlike non-system MediaRouter2, controller instances cannot be kept,
+            // since the transfer events initiated from other apps will not come through manager.
+            List<RoutingSessionInfo> sessions = sManager.getRoutingSessions(mClientPackageName);
+            for (RoutingSessionInfo session : sessions) {
+                RoutingController controller;
+                if (session.isSystemSession()) {
+                    mSystemController.setRoutingSessionInfo(session);
+                    controller = mSystemController;
+                } else {
+                    controller = new RoutingController(session);
+                }
+                result.add(controller);
+            }
+            return result;
         }
 
-        List<RoutingController> result = new ArrayList<>();
         result.add(0, mSystemController);
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             result.addAll(mNonSystemRoutingControllers.values());
         }
         return result;
@@ -599,11 +714,17 @@
      * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
      * @hide
      */
+    @SystemApi
     public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
+        if (isSystemRouter()) {
+            sManager.setRouteVolume(route, volume);
+            return;
+        }
+
         MediaRouter2Stub stub;
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             stub = mStub;
         }
         if (stub != null) {
@@ -627,7 +748,7 @@
         List<MediaRoute2Info> removedRoutes = new ArrayList<>();
         List<MediaRoute2Info> changedRoutes = new ArrayList<>();
 
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             List<String> currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId)
                     .collect(Collectors.toList());
 
@@ -685,7 +806,7 @@
 
     void addRoutesOnHandler(List<MediaRoute2Info> routes) {
         List<MediaRoute2Info> addedRoutes = new ArrayList<>();
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
                 if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
@@ -701,7 +822,7 @@
 
     void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
         List<MediaRoute2Info> removedRoutes = new ArrayList<>();
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.remove(route.getId());
                 if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
@@ -717,7 +838,7 @@
 
     void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
         List<MediaRoute2Info> changedRoutes = new ArrayList<>();
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
                 if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
@@ -789,7 +910,7 @@
             newController.setRoutingSessionInfo(sessionInfo);
         } else {
             newController = new RoutingController(sessionInfo);
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 mNonSystemRoutingControllers.put(newController.getId(), newController);
             }
         }
@@ -812,7 +933,7 @@
         }
 
         RoutingController matchingController;
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
         }
 
@@ -840,7 +961,7 @@
         }
 
         RoutingController matchingController;
-        synchronized (sRouterLock) {
+        synchronized (mLock) {
             matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
         }
 
@@ -868,7 +989,7 @@
         if (oldSession.isSystemSession()) {
             controller = getSystemController();
         } else {
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 controller = mNonSystemRoutingControllers.get(oldSession.getId());
             }
         }
@@ -878,6 +999,23 @@
         requestCreateController(controller, route, managerRequestId);
     }
 
+    /**
+     * Returns whether this router is created with {@link #getInstance(Context, String)}.
+     * This kind of router can control the target app's media routing.
+     */
+    private boolean isSystemRouter() {
+        return mClientPackageName != null;
+    }
+
+    /**
+     * Registers {@link MediaRouter2Manager.Callback} for getting events.
+     * Should only used for system media routers.
+     */
+    private void registerManagerCallbackForSystemRouter() {
+        // Using direct executor here, since MediaRouter2Manager also posts to the main handler.
+        sManager.registerCallback(Runnable::run, mManagerCallback);
+    }
+
     private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
             RouteDiscoveryPreference discoveryRequest) {
         return routes.stream()
@@ -885,6 +1023,16 @@
                 .collect(Collectors.toList());
     }
 
+    private void updateAllRoutesFromManager() {
+        synchronized (mLock) {
+            mRoutes.clear();
+            for (MediaRoute2Info route : sManager.getAllRoutes()) {
+                mRoutes.put(route.getId(), route);
+            }
+            mShouldUpdateRoutes = true;
+        }
+    }
+
     private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
             List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
@@ -915,6 +1063,13 @@
         }
     }
 
+    private void notifyPreferredFeaturesChanged(List<String> features) {
+        for (RouteCallbackRecord record: mRouteCallbackRecords) {
+            record.mExecutor.execute(
+                    () -> record.mRouteCallback.onPreferredFeaturesChanged(features));
+        }
+    }
+
     private void notifyTransfer(RoutingController oldController, RoutingController newController) {
         for (TransferCallbackRecord record: mTransferCallbackRecords) {
             record.mExecutor.execute(
@@ -968,6 +1123,17 @@
          * @param routes the list of routes that have been changed. It's never empty.
          */
         public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
+
+        /**
+         * Called when the client app's preferred features are changed.
+         * When this is called, it is recommended to {@link #getRoutes()} to get the routes
+         * that are currently available to the app.
+         *
+         * @param preferredFeatures the new preferred features set by the application
+         * @hide
+         */
+        @SystemApi
+        public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {}
     }
 
     /**
@@ -1075,6 +1241,11 @@
             mState = CONTROLLER_STATE_ACTIVE;
         }
 
+        RoutingController(@NonNull RoutingSessionInfo sessionInfo, int state) {
+            mSessionInfo = sessionInfo;
+            mState = state;
+        }
+
         /**
          * @return the ID of the controller. It is globally unique.
          */
@@ -1235,8 +1406,13 @@
                 return;
             }
 
+            if (isSystemRouter()) {
+                sManager.selectRoute(getRoutingSessionInfo(), route);
+                return;
+            }
+
             MediaRouter2Stub stub;
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 stub = mStub;
             }
             if (stub != null) {
@@ -1282,8 +1458,13 @@
                 return;
             }
 
+            if (isSystemRouter()) {
+                sManager.deselectRoute(getRoutingSessionInfo(), route);
+                return;
+            }
+
             MediaRouter2Stub stub;
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 stub = mStub;
             }
             if (stub != null) {
@@ -1318,7 +1499,7 @@
             }
 
             MediaRouter2Stub stub;
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 stub = mStub;
             }
             if (stub != null) {
@@ -1351,8 +1532,14 @@
                 Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
                 return;
             }
+
+            if (isSystemRouter()) {
+                sManager.setSessionVolume(getRoutingSessionInfo(), volume);
+                return;
+            }
+
             MediaRouter2Stub stub;
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 stub = mStub;
             }
             if (stub != null) {
@@ -1386,7 +1573,7 @@
                 mState = CONTROLLER_STATE_RELEASING;
             }
 
-            synchronized (sRouterLock) {
+            synchronized (mLock) {
                 // It could happen if the controller is released by the another thread
                 // in between two locks
                 if (!mNonSystemRoutingControllers.remove(getId(), this)) {
@@ -1415,7 +1602,12 @@
                 mState = CONTROLLER_STATE_RELEASED;
             }
 
-            synchronized (sRouterLock) {
+            if (isSystemRouter()) {
+                sManager.releaseSession(getRoutingSessionInfo());
+                return;
+            }
+
+            synchronized (mLock) {
                 mNonSystemRoutingControllers.remove(getId(), this);
 
                 if (shouldReleaseSession && mStub != null) {
@@ -1483,7 +1675,13 @@
         }
 
         private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
-            synchronized (sRouterLock) {
+            if (isSystemRouter()) {
+                return getRoutes().stream()
+                        .filter(r -> routeIds.contains(r.getId()))
+                        .collect(Collectors.toList());
+            }
+
+            synchronized (mLock) {
                 return routeIds.stream().map(mRoutes::get)
                         .filter(Objects::nonNull)
                         .collect(Collectors.toList());
@@ -1665,4 +1863,159 @@
                     MediaRouter2.this, oldSession, route, managerRequestId));
         }
     }
+
+    // Note: All methods are run on main thread.
+    class ManagerCallback implements MediaRouter2Manager.Callback {
+
+        @Override
+        public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {
+            updateAllRoutesFromManager();
+
+            List<MediaRoute2Info> filteredRoutes;
+            synchronized (mLock) {
+                filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+            }
+            if (filteredRoutes.isEmpty()) {
+                return;
+            }
+            for (RouteCallbackRecord record: mRouteCallbackRecords) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
+            }
+        }
+
+        @Override
+        public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {
+            updateAllRoutesFromManager();
+
+            List<MediaRoute2Info> filteredRoutes;
+            synchronized (mLock) {
+                filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+            }
+            if (filteredRoutes.isEmpty()) {
+                return;
+            }
+            for (RouteCallbackRecord record: mRouteCallbackRecords) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
+            }
+        }
+
+        @Override
+        public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {
+            updateAllRoutesFromManager();
+
+            List<MediaRoute2Info> filteredRoutes;
+            synchronized (mLock) {
+                filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
+            }
+            if (filteredRoutes.isEmpty()) {
+                return;
+            }
+            for (RouteCallbackRecord record: mRouteCallbackRecords) {
+                record.mExecutor.execute(
+                        () -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
+            }
+        }
+
+        @Override
+        public void onTransferred(@NonNull RoutingSessionInfo oldSession,
+                @NonNull RoutingSessionInfo newSession) {
+            if (!oldSession.isSystemSession()
+                    && !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) {
+                return;
+            }
+
+            if (!newSession.isSystemSession()
+                    && !TextUtils.equals(mClientPackageName, newSession.getClientPackageName())) {
+                return;
+            }
+
+            // For successful in-session transfer, onControllerUpdated() handles it.
+            if (TextUtils.equals(oldSession.getId(), newSession.getId())) {
+                return;
+            }
+
+
+            RoutingController oldController;
+            if (oldSession.isSystemSession()) {
+                mSystemController.setRoutingSessionInfo(oldSession);
+                oldController = mSystemController;
+            } else {
+                oldController = new RoutingController(oldSession);
+            }
+
+            RoutingController newController;
+            if (oldSession.isSystemSession()) {
+                mSystemController.setRoutingSessionInfo(newSession);
+                newController = mSystemController;
+            } else {
+                newController = new RoutingController(newSession);
+            }
+
+            notifyTransfer(oldController, newController);
+        }
+
+        @Override
+        public void onTransferFailed(@NonNull RoutingSessionInfo session,
+                @NonNull MediaRoute2Info route) {
+            if (!session.isSystemSession()
+                    && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+                return;
+            }
+            notifyTransferFailure(route);
+        }
+
+        @Override
+        public void onSessionUpdated(@NonNull RoutingSessionInfo session) {
+            if (!session.isSystemSession()
+                    && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+                return;
+            }
+
+            RoutingController controller;
+            if (session.isSystemSession()) {
+                mSystemController.setRoutingSessionInfo(session);
+                controller = mSystemController;
+            } else {
+                controller = new RoutingController(session);
+            }
+            notifyControllerUpdated(controller);
+        }
+
+        @Override
+        public void onSessionReleased(@NonNull RoutingSessionInfo session) {
+            if (session.isSystemSession()) {
+                Log.e(TAG, "onSessionReleased: Called on system session. Ignoring.");
+                return;
+            }
+
+            if (!TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
+                return;
+            }
+
+            notifyStop(new RoutingController(session, RoutingController.CONTROLLER_STATE_RELEASED));
+        }
+
+        @Override
+        public void onPreferredFeaturesChanged(@NonNull String packageName,
+                @NonNull List<String> preferredFeatures) {
+            if (!TextUtils.equals(mClientPackageName, packageName)) {
+                return;
+            }
+
+            synchronized (mLock) {
+                mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+                        preferredFeatures, true).build();
+            }
+
+            updateAllRoutesFromManager();
+            notifyPreferredFeaturesChanged(preferredFeatures);
+        }
+
+        @Override
+        public void onRequestFailed(int reason) {
+            // Does nothing.
+        }
+    }
 }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5e732f9..20e3573 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -148,7 +148,7 @@
 
     /**
      * Starts scanning remote routes.
-     * @see #stopScan(String)
+     * @see #stopScan()
      */
     public void startScan() {
         Client client = getOrCreateClient();
@@ -163,7 +163,7 @@
 
     /**
      * Stops scanning remote routes to reduce resource consumption.
-     * @see #startScan(String)
+     * @see #startScan()
      */
     public void stopScan() {
         Client client = getOrCreateClient();
@@ -237,6 +237,36 @@
     }
 
     /**
+     * Returns a list of routes which are related to the given package name in the given route list.
+     */
+    @NonNull
+    public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes,
+            @NonNull String packageName) {
+        Objects.requireNonNull(routes, "routes must not be null");
+        Objects.requireNonNull(packageName, "packageName must not be null");
+
+        List<RoutingSessionInfo> sessions = getRoutingSessions(packageName);
+        RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1);
+
+        List<MediaRoute2Info> result = new ArrayList<>();
+        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+        if (preferredFeatures == null) {
+            preferredFeatures = Collections.emptyList();
+        }
+
+        synchronized (mRoutesLock) {
+            for (MediaRoute2Info route : routes) {
+                if (route.hasAnyFeatures(preferredFeatures)
+                        || sessionInfo.getSelectedRoutes().contains(route.getId())
+                        || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+                    result.add(route);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
      * Gets the system routing session associated with no specific application.
      */
     @NonNull
@@ -758,8 +788,8 @@
      * Requests releasing a session.
      * <p>
      * If a session is released, any operation on the session will be ignored.
-     * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null}
-     * session will be called when the session is released.
+     * {@link Callback#onSessionReleased(RoutingSessionInfo)} will be called
+     * when the session is released.
      * </p>
      *
      * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
@@ -915,10 +945,10 @@
          * Called when media is transferred.
          *
          * @param oldSession the previous session
-         * @param newSession the new session or {@code null} if the session is released.
+         * @param newSession the new session
          */
         default void onTransferred(@NonNull RoutingSessionInfo oldSession,
-                @Nullable RoutingSessionInfo newSession) { }
+                @NonNull RoutingSessionInfo newSession) { }
 
         /**
          * Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails.
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 67a4a4b..fd3c405 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -16,6 +16,8 @@
 
 package android.media.audiofx;
 
+import static android.media.permission.PermissionUtil.myIdentity;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -23,11 +25,11 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioSystem;
+import android.media.permission.Identity;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -515,10 +517,11 @@
         }
 
         // native initialization
+        // TODO b/182469354: Make consistent with AudioRecord
         int initResult = native_setup(new WeakReference<AudioEffect>(this),
                 type.toString(), uuid.toString(), priority, audioSession,
                 deviceType, deviceAddress,
-                id, desc, ActivityThread.currentOpPackageName(), probe);
+                id, desc, myIdentity(null), probe);
         if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
             Log.e(TAG, "Error code " + initResult
                     + " when initializing AudioEffect.");
@@ -1385,7 +1388,7 @@
     private native final int native_setup(Object audioeffect_this, String type,
             String uuid, int priority, int audioSession,
             int deviceType, String deviceAddress, int[] id, Object[] desc,
-            String opPackageName, boolean probe);
+            Identity identity, boolean probe);
 
     private native final void native_finalize();
 
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index a5da648..58c9e65 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -16,8 +16,10 @@
 
 package android.media.audiofx;
 
-import android.app.ActivityThread;
+import static android.media.permission.PermissionUtil.myIdentity;
+
 import android.compat.annotation.UnsupportedAppUsage;
+import android.media.permission.Identity;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -217,9 +219,11 @@
 
         synchronized (mStateLock) {
             mState = STATE_UNINITIALIZED;
+
             // native initialization
+            // TODO b/182469354: make consistent with AudioRecord
             int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
-                    ActivityThread.currentOpPackageName());
+                    myIdentity(null));
             if (result != SUCCESS && result != ALREADY_EXISTS) {
                 Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
                 switch (result) {
@@ -686,7 +690,7 @@
     private native final int native_setup(Object audioeffect_this,
                                           int audioSession,
                                           int[] id,
-                                          String opPackageName);
+                                          Identity identity);
 
     @GuardedBy("mStateLock")
     private native final void native_finalize();
diff --git a/media/java/android/media/permission/PermissionUtil.java b/media/java/android/media/permission/PermissionUtil.java
index 315ee4f..92fe882 100644
--- a/media/java/android/media/permission/PermissionUtil.java
+++ b/media/java/android/media/permission/PermissionUtil.java
@@ -17,9 +17,12 @@
 package android.media.permission;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.content.Context;
 import android.content.PermissionChecker;
 import android.os.Binder;
+import android.os.Process;
 
 import java.util.Objects;
 
@@ -49,6 +52,25 @@
  */
 public class PermissionUtil {
     /**
+     * Create an identity for the current process and the passed context.
+     *
+     * @param context The process the identity is for. If {@code null}, the process's default
+     *                identity is chosen.
+     * @return The identity for the current process and context
+     */
+    public static @NonNull Identity myIdentity(@Nullable Context context) {
+        Identity identity = new Identity();
+
+        identity.pid = Process.myPid();
+        identity.uid = Process.myUid();
+        identity.packageName = context != null ? context.getOpPackageName()
+                : ActivityThread.currentOpPackageName();
+        identity.attributionTag = context != null ? context.getAttributionTag() : null;
+
+        return identity;
+    }
+
+    /**
      * Authenticate an originator, where the binder call is coming from a middleman.
      *
      * The middleman is expected to hold a special permission to act as such, or else a
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ce45504..f09dcde 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -84,6 +84,7 @@
         "android.hardware.drm@1.4",
         "android.hidl.memory@1.0",
         "android.hidl.token@1.0-utils",
+        "media_permission-aidl-cpp",
     ],
 
     header_libs: [
@@ -182,7 +183,7 @@
         "libnativehelper",
         "libutils",
         "tv_tuner_aidl_interface-ndk_platform",
-        "tv_tuner_resource_manager_aidl_interface-ndk_platform"
+        "tv_tuner_resource_manager_aidl_interface-ndk_platform",
     ],
 
     static_libs: [
@@ -212,4 +213,3 @@
         "-Wunreachable-code",
     ],
 }
-
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 98ac5b9..a360759 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -17,6 +17,7 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaPlayer-JNI"
+#include "permission_utils.h"
 #include "utils/Log.h"
 
 #include <media/mediaplayer.h>
@@ -79,6 +80,8 @@
 using namespace android;
 
 using media::VolumeShaper;
+using media::permission::Identity;
+using media::permission::convertIdentity;
 
 // ----------------------------------------------------------------------------
 
@@ -946,11 +949,11 @@
 
 static void
 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-                                       jstring opPackageName)
+                                       jobject jIdentity)
 {
     ALOGV("native_setup");
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
-    sp<MediaPlayer> mp = new MediaPlayer(opPackageNameStr.c_str());
+
+    sp<MediaPlayer> mp = new MediaPlayer(convertIdentity(env, jIdentity));
     if (mp == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
@@ -1406,7 +1409,7 @@
     {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
     {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
-    {"native_setup",        "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
+    {"native_setup",        "(Ljava/lang/Object;Landroid/media/permission/Identity;)V",(void *)android_media_MediaPlayer_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
     {"native_setAudioSessionId",   "(I)V",                      (void *)android_media_MediaPlayer_set_audio_session_id},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index f99dc01..6641123 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -24,6 +24,7 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaRecorderJNI"
+#include "permission_utils.h"
 #include <utils/Log.h>
 
 #include <gui/Surface.h>
@@ -50,6 +51,8 @@
 
 using namespace android;
 
+using android::media::permission::convertIdentity;
+
 // ----------------------------------------------------------------------------
 
 // helper function to extract a native Camera object from a Camera Java object
@@ -617,13 +620,12 @@
 
 static void
 android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-                                         jstring packageName, jstring opPackageName)
+                                         jstring packageName, jobject jIdentity)
 {
     ALOGV("setup");
 
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
+    sp<MediaRecorder> mr = new MediaRecorder(convertIdentity(env, jIdentity));
 
-    sp<MediaRecorder> mr = new MediaRecorder(String16(opPackageNameStr.c_str()));
     if (mr == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
@@ -869,7 +871,7 @@
     {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
     {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
     {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
-    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
+    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Landroid/media/permission/Identity;)V",
                                                                 (void *)android_media_MediaRecorder_native_setup},
     {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
     {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index c2fc91d..bfed983 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -27,6 +27,11 @@
         "libaudioclient",
         "libaudioutils",
         "libaudiofoundation",
+        "media_permission-aidl-cpp",
+    ],
+
+    export_shared_lib_headers: [
+        "media_permission-aidl-cpp",
     ],
 
     version_script: "exports.lds",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index a74ae53..8a52456 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -34,8 +34,8 @@
 
 // ---------------------------------------------------------------------------
 
-Visualizer::Visualizer (const String16& opPackageName)
-        :   AudioEffect(opPackageName)
+Visualizer::Visualizer (const Identity& identity)
+        :   AudioEffect(identity)
 {
 }
 
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index 8b6a62f..3ee91f0 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,6 +20,9 @@
 #include <media/AudioEffect.h>
 #include <system/audio_effects/effect_visualizer.h>
 #include <utils/Thread.h>
+#include "android/media/permission/Identity.h"
+
+using namespace android::media::permission;
 
 /**
  * The Visualizer class enables application to retrieve part of the currently playing audio for
@@ -65,7 +68,7 @@
     /* Constructor.
      * See AudioEffect constructor for details on parameters.
      */
-                        explicit Visualizer(const String16& opPackageName);
+                        explicit Visualizer(const Identity& identity);
 
                         ~Visualizer();
 
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 0d53ab1..953b7e0 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -25,6 +25,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include "media/AudioEffect.h"
+#include "permission_utils.h"
 
 #include <nativehelper/ScopedUtfChars.h>
 
@@ -34,6 +35,8 @@
 
 using namespace android;
 
+using media::permission::convertIdentity;
+
 #define AUDIOEFFECT_SUCCESS                      0
 #define AUDIOEFFECT_ERROR                       (-1)
 #define AUDIOEFFECT_ERROR_ALREADY_EXISTS        (-2)
@@ -270,7 +273,7 @@
 android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
         jstring type, jstring uuid, jint priority, jint sessionId,
         jint deviceType, jstring deviceAddress,
-        jintArray jId, jobjectArray javadesc, jstring opPackageName, jboolean probe)
+        jintArray jId, jobjectArray javadesc, jobject jIdentity, jboolean probe)
 {
     ALOGV("android_media_AudioEffect_native_setup");
     AudioEffectJniStorage* lpJniStorage = NULL;
@@ -283,8 +286,6 @@
     jobject jdesc;
     AudioDeviceTypeAddr device;
 
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
-
     setAudioEffect(env, thiz, 0);
 
     if (type != NULL) {
@@ -337,7 +338,7 @@
     }
 
     // create the native AudioEffect object
-    lpAudioEffect = new AudioEffect(String16(opPackageNameStr.c_str()));
+    lpAudioEffect = new AudioEffect(convertIdentity(env, jIdentity));
     if (lpAudioEffect == 0) {
         ALOGE("Error creating AudioEffect");
         goto setup_failure;
@@ -773,7 +774,7 @@
 // Dalvik VM type signatures
 static const JNINativeMethod gMethods[] = {
     {"native_init",          "()V",      (void *)android_media_AudioEffect_native_init},
-    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Ljava/lang/String;Z)I",
+    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Landroid/media/permission/Identity;Z)I",
                                          (void *)android_media_AudioEffect_native_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioEffect_native_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioEffect_native_release},
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 4c5970a..439715c 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -25,6 +25,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 #include "Visualizer.h"
+#include "permission_utils.h"
 
 #include <nativehelper/ScopedUtfChars.h>
 
@@ -347,7 +348,7 @@
 
 static jint
 android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-        jint sessionId, jintArray jId, jstring opPackageName)
+        jint sessionId, jintArray jId, jobject jIdentity)
 {
     ALOGV("android_media_visualizer_native_setup");
     VisualizerJniStorage* lpJniStorage = NULL;
@@ -355,8 +356,6 @@
     sp<Visualizer> lpVisualizer;
     jint* nId = NULL;
 
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
-
     setVisualizer(env, thiz, 0);
 
     lpJniStorage = new VisualizerJniStorage();
@@ -382,7 +381,7 @@
     }
 
     // create the native Visualizer object
-    lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str()));
+    lpVisualizer = new Visualizer(convertIdentity(env, jIdentity));
     if (lpVisualizer == 0) {
         ALOGE("Error creating Visualizer");
         goto setup_failure;
@@ -679,7 +678,7 @@
 // Dalvik VM type signatures
 static const JNINativeMethod gMethods[] = {
     {"native_init",            "()V",     (void *)android_media_visualizer_native_init},
-    {"native_setup",           "(Ljava/lang/Object;I[ILjava/lang/String;)I",
+    {"native_setup",           "(Ljava/lang/Object;I[ILandroid/media/permission/Identity;)I",
                                           (void *)android_media_visualizer_native_setup},
     {"native_finalize",          "()V",   (void *)android_media_visualizer_native_finalize},
     {"native_release",           "()V",   (void *)android_media_visualizer_native_release},
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index b3406cd..4227cd8 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -63,7 +63,7 @@
 
     // Remove some pedantic stylistic requirements.
     "-google-readability-casting", // C++ casts not always necessary and may be verbose
-    "-google-readability-todo",    // do not require TODO(info)
+    "-google-readability-todo", // do not require TODO(info)
     "-google-build-using-namespace", // Reenable and fix later.
 
     "-google-explicit-constructor", // found in StreamManager.h
@@ -100,7 +100,7 @@
     tidy_checks: tidy_errors,
     tidy_checks_as_errors: tidy_errors,
     tidy_flags: [
-      "-format-style=file",
+        "-format-style=file",
     ],
 }
 
@@ -135,6 +135,7 @@
         "libaudioclient",
         "libmediandk",
         "libbinder",
+        "media_permission-aidl-cpp",
     ],
 
     cflags: [
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
index f8b4bdb..50e0d33 100644
--- a/media/jni/soundpool/Sound.cpp
+++ b/media/jni/soundpool/Sound.cpp
@@ -99,8 +99,8 @@
                                     __func__);
                             break;
                         }
-                        int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
-                        ALOGV("%s: read %d", __func__, sampleSize);
+                        ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+                        ALOGV("%s: read %zd", __func__, sampleSize);
                         if (sampleSize < 0) {
                             sampleSize = 0;
                             sawInputEOS = true;
@@ -124,8 +124,8 @@
                 }
 
                 AMediaCodecBufferInfo info;
-                const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
-                ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+                const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+                ALOGV("%s: dequeueoutput returned: %zd", __func__, status);
                 if (status >= 0) {
                     if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
                         ALOGV("%s: output EOS", __func__);
@@ -167,10 +167,10 @@
                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
                     ALOGV("%s: no output buffer right now", __func__);
                 } else if (status <= AMEDIA_ERROR_BASE) {
-                    ALOGE("%s: decode error: %d", __func__, status);
+                    ALOGE("%s: decode error: %zd", __func__, status);
                     break;
                 } else {
-                    ALOGV("%s: unexpected info code: %d", __func__, status);
+                    ALOGV("%s: unexpected info code: %zd", __func__, status);
                 }
             }
 
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 73e319a..95fe000 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "SoundPool::Stream"
 #include <utils/Log.h>
+#include<android/media/permission/Identity.h>
 
 #include "Stream.h"
 
@@ -24,6 +25,8 @@
 
 namespace android::soundpool {
 
+using media::permission::Identity;
+
 Stream::~Stream()
 {
     ALOGV("%s(%p)", __func__, this);
@@ -317,7 +320,8 @@
             // audio track while the new one is being started and avoids processing them with
             // wrong audio audio buffer size  (mAudioBufferSize)
             auto toggle = mToggle ^ 1;
-            void* userData = (void*)((uintptr_t)this | toggle);
+            // NOLINTNEXTLINE(performance-no-int-to-ptr)
+            void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
             audio_channel_mask_t soundChannelMask = sound->getChannelMask();
             // When sound contains a valid channel mask, use it as is.
             // Otherwise, use stream count to calculate channel mask.
@@ -326,15 +330,17 @@
 
             // do not create a new audio track if current track is compatible with sound parameters
 
+            Identity identity = Identity();
+            identity.packageName = mStreamManager->getOpPackageName();
+            // TODO b/182469354 make consistent with AudioRecord, add util for native source
             newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
                     channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
                     staticCallback, userData,
                     0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
                     AudioTrack::TRANSFER_DEFAULT,
-                    nullptr /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/,
+                    nullptr /*offloadInfo*/, identity,
                     mStreamManager->getAttributes(),
-                    false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/,
-                    mStreamManager->getOpPackageName());
+                    false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
             // Set caller name so it can be logged in destructor.
             // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
             newTrack->setCallerName("soundpool");
@@ -386,6 +392,7 @@
 void Stream::staticCallback(int event, void* user, void* info)
 {
     const auto userAsInt = (uintptr_t)user;
+    // NOLINTNEXTLINE(performance-no-int-to-ptr)
     auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
     stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
 }
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 502ee00..8b84bf3 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -330,7 +330,7 @@
 
     // streams on mProcessingStreams are undergoing processing by the StreamManager thread
     // and do not participate in normal stream migration.
-    return found;
+    return (ssize_t)found;
 }
 
 void StreamManager::addToRestartQueue_l(Stream *stream) {
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 357cc63..a66d99f 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -34,7 +34,8 @@
     jclass      mSoundPoolClass;
 } fields;
 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
-    return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
+    // NOLINTNEXTLINE(performance-no-int-to-ptr)
+    return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext));
 }
 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
 struct audio_attributes_fields_t {
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index a8f1a4d..243e4ca 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -143,6 +143,7 @@
 
   public static class ConnectivityManager.NetworkCallback {
     ctor public ConnectivityManager.NetworkCallback();
+    ctor public ConnectivityManager.NetworkCallback(int);
     method public void onAvailable(@NonNull android.net.Network);
     method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
     method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
@@ -150,6 +151,7 @@
     method public void onLosing(@NonNull android.net.Network, int);
     method public void onLost(@NonNull android.net.Network);
     method public void onUnavailable();
+    field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1
   }
 
   public static interface ConnectivityManager.OnNetworkActiveListener {
@@ -293,6 +295,7 @@
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public int getOwnerUid();
     method public int getSignalStrength();
+    method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
     method @Nullable public android.net.TransportInfo getTransportInfo();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
@@ -399,6 +402,11 @@
     method public android.net.NetworkRequest.Builder removeTransportType(int);
     method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
     method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+    method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public String response;
   }
 
   public class ProxyInfo implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 6df57c1..a045714 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -7,9 +7,14 @@
 
   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 @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
+    method @NonNull public static String getPrivateDnsMode(@NonNull android.content.ContentResolver);
     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.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull 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);
+    field public static final String PRIVATE_DNS_MODE_OFF = "off";
+    field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+    field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
   }
 
   public final class NetworkAgentConfig implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index a732430..a98f14e 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -296,6 +296,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
     method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
   }
 
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index bbf4559..66c45ed 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -16,13 +16,15 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
 import static android.net.NetworkRequest.Type.LISTEN;
 import static android.net.NetworkRequest.Type.REQUEST;
+import static android.net.NetworkRequest.Type.TRACK_BEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
 import static android.net.QosCallback.QosCallbackRegistrationException;
+import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -31,11 +33,13 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -43,6 +47,7 @@
 import android.net.TetheringManager.StartTetheringCallback;
 import android.net.TetheringManager.TetheringEventCallback;
 import android.net.TetheringManager.TetheringRequest;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
@@ -62,6 +67,7 @@
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Range;
@@ -801,24 +807,27 @@
     /**
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final String PRIVATE_DNS_MODE_OFF = "off";
     /**
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
     /**
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
-    /**
-     * The default Private DNS mode.
-     *
-     * This may change from release to release or may become dependent upon
-     * the capabilities of the underlying platform.
-     *
-     * @hide
-     */
-    public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(value = {
+            PRIVATE_DNS_MODE_OFF,
+            PRIVATE_DNS_MODE_OPPORTUNISTIC,
+            PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+    })
+    public @interface PrivateDnsMode {}
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
     private final IConnectivityManager mService;
@@ -1314,7 +1323,7 @@
     }
 
     /**
-     * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
+     * Returns an array of {@link NetworkCapabilities} objects, representing
      * the Networks that applications run by the given user will use by default.
      * @hide
      */
@@ -1394,11 +1403,19 @@
     }
 
     /**
-     * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}.  This
+     * Get the {@link NetworkCapabilities} for the given {@link Network}.  This
      * will return {@code null} if the network is unknown.
      *
+     * This will remove any location sensitive data in {@link TransportInfo} embedded in
+     * {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like
+     * {@link android.net.wifi.WifiInfo} contain location sensitive information. Retrieving
+     * this location sensitive information (subject to app's location permissions) will be
+     * noted by system. To include any location sensitive data in {@link TransportInfo},
+     * use a {@link NetworkCallback} with
+     * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} flag.
+     *
      * @param network The {@link Network} object identifying the network in question.
-     * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
+     * @return The {@link NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @Nullable
@@ -1996,7 +2013,7 @@
             dup = createInvalidFd();
         }
         return new NattSocketKeepalive(mService, network, dup,
-                INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
+                -1 /* Unused */, source, destination, executor, callback);
     }
 
     /**
@@ -3244,6 +3261,54 @@
      */
     public static class NetworkCallback {
         /**
+         * No flags associated with this callback.
+         * @hide
+         */
+        public static final int FLAG_NONE = 0;
+        /**
+         * Use this flag to include any location sensitive data in {@link NetworkCapabilities} sent
+         * via {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}.
+         * <p>
+         * These include:
+         * <li> Some transport info instances (retrieved via
+         * {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
+         * contain location sensitive information.
+         * <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
+         * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+         * </p>
+         * <p>
+         * Note:
+         * <li> Retrieving this location sensitive information (subject to app's location
+         * permissions) will be noted by system. </li>
+         * <li> Without this flag any {@link NetworkCapabilities} provided via the callback does
+         * not include location sensitive info.
+         * </p>
+         */
+        public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = "FLAG_", value = {
+                FLAG_NONE,
+                FLAG_INCLUDE_LOCATION_INFO
+        })
+        public @interface Flag { }
+
+        /**
+         * All the valid flags for error checking.
+         */
+        private static final int VALID_FLAGS = FLAG_INCLUDE_LOCATION_INFO;
+
+        public NetworkCallback() {
+            this(FLAG_NONE);
+        }
+
+        public NetworkCallback(@Flag int flags) {
+            Preconditions.checkArgument((flags & VALID_FLAGS) == flags);
+            mFlags = flags;
+        }
+
+        /**
          * Called when the framework connects to a new network to evaluate whether it satisfies this
          * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
          * callback. There is no guarantee that this new network will satisfy any requests, or that
@@ -3380,7 +3445,7 @@
          * calling these methods while in a callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} whose capabilities have changed.
-         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+         * @param networkCapabilities The new {@link NetworkCapabilities} for this
          *                            network.
          */
         public void onCapabilitiesChanged(@NonNull Network network,
@@ -3449,6 +3514,7 @@
         public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
 
         private NetworkRequest networkRequest;
+        private final int mFlags;
     }
 
     /**
@@ -3638,14 +3704,15 @@
                 }
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
+                final int callbackFlags = callback.mFlags;
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName,
+                            need, messenger, binder, callbackFlags, callingPackageName,
                             getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
                             need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
-                            callingPackageName, getAttributionTag());
+                            callbackFlags, callingPackageName, getAttributionTag());
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -3692,7 +3759,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * <p>This method will attempt to find the best network that matches the passed
      * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
@@ -3776,7 +3843,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
      * but runs all the callbacks on the passed Handler.
@@ -3798,7 +3865,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This function behaves identically to the non-timed-out version
@@ -3833,7 +3900,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This method behaves identically to
@@ -3878,7 +3945,7 @@
 
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This function behaves identically to the version that takes a NetworkCallback, but instead
      * of {@link NetworkCallback} a {@link PendingIntent} is used.  This means
@@ -4190,6 +4257,18 @@
     }
 
     /**
+     * @hide
+     */
+    // TODO: Make it public api.
+    @SuppressLint("ExecutorRegistration")
+    public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+        final NetworkCapabilities nc = request.networkCapabilities;
+        final CallbackHandler cbHandler = new CallbackHandler(handler);
+        sendRequestForNetwork(nc, networkCallback, 0, TRACK_BEST, TYPE_NONE, cbHandler);
+    }
+
+    /**
      * Requests bandwidth update for a given {@link Network} and returns whether the update request
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
@@ -4898,7 +4977,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, but
      * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
      * be used to request that the system provide a network without causing the network to be
      * in the foreground.
@@ -4979,10 +5058,10 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
     })
     public void requestBackgroundNetwork(@NonNull NetworkRequest request,
-            @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+            @NonNull Handler handler, @NonNull NetworkCallback networkCallback) {
         final NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
-                TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
+                TYPE_NONE, new CallbackHandler(handler));
     }
 
     /**
@@ -5040,4 +5119,41 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    // The first network ID of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_START = 0xFC00;
+    // The network ID range of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_RANGE = 0x0400;
+
+    /**
+     * Get the network ID range reserved for IPSec tunnel interfaces.
+     *
+     * @return A Range which indicates the network ID range of IPSec tunnel interface.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    public static Range<Integer> getIpSecNetIdRange() {
+        return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
+    }
+
+    /**
+     * Get private DNS mode from settings.
+     *
+     * @param cr The ContentResolver to query private DNS mode from settings.
+     * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    @PrivateDnsMode
+    public static String getPrivateDnsMode(@NonNull ContentResolver cr) {
+        String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
+        if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
+        // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
+        // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
+        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+        return mode;
+    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index cd49258..f9393e3 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -143,7 +143,7 @@
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
-            String callingPackageName, String callingAttributionTag);
+            int callbackFlags, String callingPackageName, String callingAttributionTag);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
             in PendingIntent operation, String callingPackageName, String callingAttributionTag);
@@ -151,7 +151,7 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName,
+            in Messenger messenger, in IBinder binder, int callbackFlags, String callingPackageName,
             String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
index d2ee7d1..bf4481a 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -113,7 +113,7 @@
         // first statement in constructor". We could factor out setting the member variables to an
         // init() method, but if we did, then we'd have to make the members non-final, or "error:
         // cannot assign a value to final variable address". So we just duplicate the code here.
-        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(prefix);
         this.address = ipAndMask.first.getAddress();
         this.prefixLength = ipAndMask.second;
         checkAndMaskAddressAndPrefixLength();
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
index d1bdaa0..d48b8c7 100644
--- a/packages/Connectivity/framework/src/android/net/LinkAddress.java
+++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java
@@ -325,7 +325,7 @@
     public LinkAddress(@NonNull String address, int flags, int scope) {
         // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
         // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
-        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(address);
         init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c82cd3b..058f3c9 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -131,6 +132,7 @@
         mPrivateDnsBroken = false;
         mRequestorUid = Process.INVALID_UID;
         mRequestorPackageName = null;
+        mSubIds = new ArraySet<>();
     }
 
     /**
@@ -159,6 +161,7 @@
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
         mRequestorUid = nc.mRequestorUid;
         mRequestorPackageName = nc.mRequestorPackageName;
+        mSubIds = new ArraySet<>(nc.mSubIds);
     }
 
     /**
@@ -1048,6 +1051,16 @@
      *
      * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
      * this field cleared out.
+     *
+     * <p>
+     * This field will only be populated for VPN and wifi network suggestor apps (i.e using
+     * {@link WifiNetworkSuggestion}), and only for the network they own.
+     * In the case of wifi network suggestors apps, this field is also location sensitive, so the
+     * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
+     * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
+     * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
+     * callback. The app will be blamed for location access if this field is included.
+     * </p>
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -1655,6 +1668,7 @@
         combineSSIDs(nc);
         combineRequestor(nc);
         combineAdministratorUids(nc);
+        combineSubIds(nc);
     }
 
     /**
@@ -1674,8 +1688,9 @@
                 && satisfiedBySpecifier(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
-                && (onlyImmutable || satisfiedBySSID(nc)))
-                && (onlyImmutable || satisfiedByRequestor(nc));
+                && (onlyImmutable || satisfiedBySSID(nc))
+                && (onlyImmutable || satisfiedByRequestor(nc))
+                && (onlyImmutable || satisfiedBySubIds(nc)));
     }
 
     /**
@@ -1771,7 +1786,8 @@
                 && equalsOwnerUid(that)
                 && equalsPrivateDnsBroken(that)
                 && equalsRequestor(that)
-                && equalsAdministratorUids(that);
+                && equalsAdministratorUids(that)
+                && equalsSubIds(that);
     }
 
     @Override
@@ -1793,7 +1809,8 @@
                 + Objects.hashCode(mPrivateDnsBroken) * 47
                 + Objects.hashCode(mRequestorUid) * 53
                 + Objects.hashCode(mRequestorPackageName) * 59
-                + Arrays.hashCode(mAdministratorUids) * 61;
+                + Arrays.hashCode(mAdministratorUids) * 61
+                + Objects.hashCode(mSubIds) * 67;
     }
 
     @Override
@@ -1827,6 +1844,7 @@
         dest.writeInt(mOwnerUid);
         dest.writeInt(mRequestorUid);
         dest.writeString(mRequestorPackageName);
+        dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1850,6 +1868,11 @@
                 netCap.mOwnerUid = in.readInt();
                 netCap.mRequestorUid = in.readInt();
                 netCap.mRequestorPackageName = in.readString();
+                netCap.mSubIds = new ArraySet<>();
+                final int[] subIdInts = Objects.requireNonNull(in.createIntArray());
+                for (int i = 0; i < subIdInts.length; i++) {
+                    netCap.mSubIds.add(subIdInts[i]);
+                }
                 return netCap;
             }
             @Override
@@ -1933,11 +1956,14 @@
             sb.append(" SSID: ").append(mSSID);
         }
 
-
         if (mPrivateDnsBroken) {
             sb.append(" PrivateDnsBroken");
         }
 
+        if (!mSubIds.isEmpty()) {
+            sb.append(" SubscriptionIds: ").append(mSubIds);
+        }
+
         sb.append("]");
         return sb.toString();
     }
@@ -2251,6 +2277,67 @@
     }
 
     /**
+     * Set of the subscription IDs that identifies the network or request, empty if none.
+     */
+    @NonNull
+    private ArraySet<Integer> mSubIds = new ArraySet<>();
+
+    /**
+     * Sets the subscription ID set that associated to this network or request.
+     *
+     * @hide
+     */
+    @NonNull
+    public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+        mSubIds = new ArraySet(Objects.requireNonNull(subIds));
+        return this;
+    }
+
+    /**
+     * Gets the subscription ID set that associated to this network or request.
+     * @return
+     */
+    @NonNull
+    public Set<Integer> getSubIds() {
+        return new ArraySet<>(mSubIds);
+    }
+
+    /**
+     * Tests if the subscription ID set of this network is the same as that of the passed one.
+     */
+    private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+        return Objects.equals(mSubIds, nc.mSubIds);
+    }
+
+    /**
+     * Check if the subscription ID set requirements of this object are matched by the passed one.
+     * If specified in the request, the passed one need to have at least one subId and at least
+     * one of them needs to be in the request set.
+     */
+    private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+        if (mSubIds.isEmpty()) return true;
+        if (nc.mSubIds.isEmpty()) return false;
+        for (final Integer subId : nc.mSubIds) {
+            if (mSubIds.contains(subId)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Combine subscription ID set of the capabilities.
+     *
+     * <p>This is only legal if the subscription Ids are equal.
+     *
+     * <p>If both subscription IDs are not equal, they belong to different subscription
+     * (or no subscription). In this case, it would not make sense to add them together.
+     */
+    private void combineSubIds(@NonNull NetworkCapabilities nc) {
+        if (!Objects.equals(mSubIds, nc.mSubIds)) {
+            throw new IllegalStateException("Can't combine two subscription ID sets");
+        }
+    }
+
+    /**
      * Builder class for NetworkCapabilities.
      *
      * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
@@ -2556,6 +2643,18 @@
         }
 
         /**
+         * Set the subscription ID set.
+         *
+         * @param subIds a set that represent the subscription IDs. Empty if clean up.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull final Set<Integer> subIds) {
+            mCaps.setSubIds(subIds);
+            return this;
+        }
+
+        /**
          * Builds the instance of the capabilities.
          *
          * @return the built instance of NetworkCapabilities.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
index d752901..bb23494 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkInfo.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
@@ -21,7 +21,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -164,7 +163,7 @@
      * @param typeName a human-readable string for the network type, or an empty string or null.
      * @param subtypeName a human-readable string for the subtype, or an empty string or null.
      */
-    public NetworkInfo(int type, @NetworkType int subtype,
+    public NetworkInfo(int type, int subtype,
             @Nullable String typeName, @Nullable String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)
                 && type != ConnectivityManager.TYPE_NONE) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 17a8ee1..3fd95ee 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -114,6 +114,10 @@
      *       for the network (if any) that satisfies the default Internet
      *       request.
      *
+     *     - TRACK_BEST, which causes the framework to send callbacks about
+     *       the single, highest scoring current network (if any) that matches
+     *       the specified NetworkCapabilities.
+     *
      *     - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
      *       to retain the NET_CAPABILITY_FOREGROUND capability. A network with
      *       no foreground requests is in the background. A network that has
@@ -136,6 +140,7 @@
         REQUEST,
         BACKGROUND_REQUEST,
         TRACK_SYSTEM_DEFAULT,
+        TRACK_BEST,
     };
 
     /**
@@ -456,6 +461,21 @@
             }
             nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         }
+
+        /**
+         * Sets the optional subscription ID set.
+         * <p>
+         * This specify the subscription IDs requirement.
+         * A network will satisfy this request only if it matches one of the subIds in this set.
+         * An empty set matches all networks, including those without a subId.
+         *
+         * @param subIds A {@code Set} that represents subscription IDs.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull Set<Integer> subIds) {
+            mNetworkCapabilities.setSubIds(subIds);
+            return this;
+        }
     }
 
     // implement the Parcelable interface
diff --git a/packages/Connectivity/framework/src/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java
index d010265..9b69674 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkState.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkState.java
@@ -22,7 +22,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Slog;
+import android.util.Log;
 
 /**
  * Snapshot of network state.
@@ -83,7 +83,7 @@
         if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
-                Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
+                Log.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
                         + " and " + networkCapabilities);
             }
         }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 9e42bbe..c0f2628 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -27,8 +27,10 @@
 import java.io.FileDescriptor;
 import java.math.BigInteger;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
+import java.net.UnknownHostException;
 import java.util.Locale;
 import java.util.TreeSet;
 
@@ -212,7 +214,7 @@
     @Deprecated
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
-        return InetAddress.parseNumericAddress(addrString);
+        return InetAddresses.parseNumericAddress(addrString);
     }
 
     /**
@@ -234,7 +236,7 @@
         try {
             String[] pieces = ipAndMaskString.split("/", 2);
             prefixLength = Integer.parseInt(pieces[1]);
-            address = InetAddress.parseNumericAddress(pieces[0]);
+            address = InetAddresses.parseNumericAddress(pieces[0]);
         } catch (NullPointerException e) {            // Null string.
         } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
         } catch (NumberFormatException e) {           // Non-numeric prefix.
@@ -249,6 +251,47 @@
     }
 
     /**
+     * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+     * @hide
+     *
+     * @deprecated This method is used only for IpPrefix and LinkAddress. Since Android S, use
+     *             {@link #parseIpAndMask(String)}, if possible.
+     */
+    @Deprecated
+    public static Pair<InetAddress, Integer> legacyParseIpAndMask(String ipAndMaskString) {
+        InetAddress address = null;
+        int prefixLength = -1;
+        try {
+            String[] pieces = ipAndMaskString.split("/", 2);
+            prefixLength = Integer.parseInt(pieces[1]);
+            if (pieces[0] == null || pieces[0].isEmpty()) {
+                final byte[] bytes = new byte[16];
+                bytes[15] = 1;
+                return new Pair<InetAddress, Integer>(Inet6Address.getByAddress(
+                        "ip6-localhost"/* host */, bytes, 0 /* scope_id */), prefixLength);
+            }
+
+            if (pieces[0].startsWith("[")
+                    && pieces[0].endsWith("]")
+                    && pieces[0].indexOf(':') != -1) {
+                pieces[0] = pieces[0].substring(1, pieces[0].length() - 1);
+            }
+            address = InetAddresses.parseNumericAddress(pieces[0]);
+        } catch (NullPointerException e) {            // Null string.
+        } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
+        } catch (NumberFormatException e) {           // Non-numeric prefix.
+        } catch (IllegalArgumentException e) {        // Invalid IP address.
+        } catch (UnknownHostException e) {            // IP address length is illegal
+        }
+
+        if (address == null || prefixLength == -1) {
+            throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+        }
+
+        return new Pair<InetAddress, Integer>(address, prefixLength);
+    }
+
+    /**
      * Convert a 32 char hex string into a Inet6Address.
      * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
      * made into an Inet6Address
diff --git a/core/java/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
similarity index 100%
rename from core/java/android/net/ParseException.java
rename to packages/Connectivity/framework/src/android/net/ParseException.java
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 229db0d..745e20f 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -129,7 +129,7 @@
     }
 
     /**
-     * Only used in PacProxyInstaller after Local Proxy is bound.
+     * Only used in PacProxyService after Local Proxy is bound.
      * @hide
      */
     public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
index d37c469..53d9669 100644
--- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
@@ -92,7 +92,7 @@
         Objects.requireNonNull(socket, "socket cannot be null");
 
         mNetwork = Objects.requireNonNull(network, "network cannot be null");
-        mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+        mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
         mLocalSocketAddress =
                 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
     }
@@ -114,10 +114,10 @@
         try {
             return new InetSocketAddress(InetAddress.getByAddress(address), port);
         } catch (final UnknownHostException e) {
-            /* The catch block was purposely left empty.  UnknownHostException will never be thrown
+            /* This can never happen. UnknownHostException will never be thrown
                since the address provided is numeric and non-null. */
+            throw new RuntimeException("UnknownHostException on numeric address", e);
         }
-        return new InetSocketAddress();
     }
 
     @Override
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index 340141b..c510079 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -42,9 +42,9 @@
             MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
 
     /** Type of this VPN. */
-    @VpnManager.VpnType public final int type;
+    public final int type;
 
-    public VpnTransportInfo(@VpnManager.VpnType int type) {
+    public VpnTransportInfo(int type) {
         this.type = type;
     }
 
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 856f3b8..f630cea 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -60,6 +60,7 @@
         "services.core",
         "services.net",
         "unsupportedappusage",
+        "ServiceConnectivityResources",
     ],
     static_libs: [
         "modules-utils-os",
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
new file mode 100644
index 0000000..f2446b7
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+// APK to hold all the wifi overlayable resources.
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+    name: "ServiceConnectivityResources",
+    sdk_version: "system_current",
+    resource_dirs: [
+        "res",
+    ],
+    privileged: true,
+    export_package_resources: true,
+    apex_available: [
+        "com.android.tethering",
+    ],
+    // TODO: use a dedicated cert once generated
+    certificate: "platform",
+}
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
new file mode 100644
index 0000000..2c30302
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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.
+ */
+-->
+<!-- Manifest for connectivity resources APK -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.connectivity.resources"
+          coreApp="true"
+          android:versionCode="1"
+          android:versionName="S-initial">
+    <application
+        android:label="@string/connectivityResourcesAppLabel"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true">
+        <!-- This is only used to identify this app by resolving the action.
+             The activity is never actually triggered. -->
+        <activity android:name="android.app.Activity" android:exported="true" android:enabled="true">
+            <intent-filter>
+                <action android:name="com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
new file mode 100644
index 0000000..7d98c76
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -0,0 +1,45 @@
+<?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.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+
+    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
+         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+         and if that value is empty, the framework will use a hard-coded default.
+         This is *NOT* a URL that will always be used by the system network validation to detect
+         captive portals: NetworkMonitor may use different strategies and will not necessarily use
+         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
+         instead. -->
+    <!--suppress CheckTagEmptyBody -->
+    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
+
+    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
+    <integer name="config_networkTransitionTimeout">60000</integer>
+
+    <!-- Configuration of network interfaces that support WakeOnLAN -->
+    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+        <!--
+        <item>wlan0</item>
+        <item>eth0</item>
+        -->
+    </string-array>
+
+</resources>
\ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
new file mode 100644
index 0000000..00ec2df
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <overlayable name="ServiceConnectivityResourcesConfig">
+        <policy type="product|system|vendor">
+            <!-- Configuration values for ConnectivityService -->
+            <item type="string" name="config_networkCaptivePortalServerUrl"/>
+            <item type="integer" name="config_networkTransitionTimeout"/>
+            <item type="array" name="config_wakeonlan_supported_interfaces"/>
+
+
+        </policy>
+    </overlayable>
+</resources>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
copy to packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
index 644d1ada..2c7b992 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
@@ -14,9 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
-    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.UdfpsAnimationViewKeyguard>
+<resources>
+    <!-- The System Connectivity Resources package is an internal system package that provides
+         configuration values for system networking that were pre-configured in the device. This
+         is the name of the package to display in the list of system apps. [CHAR LIMIT=40] -->
+    <string name="connectivityResourcesAppLabel">System Connectivity Resources</string>
+</resources>
\ No newline at end of file
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 180dfb6..784a747 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index d87ea7f..5b7bda4 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 180dfb6..784a747 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 3124651..780cb8a 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index c1dca5d..16a946d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -138,9 +138,6 @@
     private long mCurrentPartitionSize;
     private long mCurrentPartitionInstalledSize;
 
-    private boolean mJustCancelledByUser;
-    private boolean mKeepNotification;
-
     // This is for testing only now
     private boolean mEnableWhenCompleted;
 
@@ -174,11 +171,6 @@
         if (cache != null) {
             cache.flush();
         }
-
-        if (!mKeepNotification) {
-            // Cancel the persistent notification.
-            mNM.cancel(NOTIFICATION_ID);
-        }
     }
 
     @Override
@@ -231,9 +223,11 @@
             return;
         }
 
+        boolean removeNotification = false;
         switch (result) {
             case RESULT_CANCELLED:
                 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+                removeNotification = true;
                 break;
 
             case RESULT_ERROR_IO:
@@ -251,7 +245,7 @@
         }
 
         // if it's not successful, reset the task and stop self.
-        resetTaskAndStop();
+        resetTaskAndStop(removeNotification);
     }
 
     private void executeInstallCommand(Intent intent) {
@@ -302,12 +296,12 @@
             return;
         }
 
-        stopForeground(true);
-        mJustCancelledByUser = true;
-
         if (mInstallTask.cancel(false)) {
-            // Will stopSelf() in onResult()
+            // onResult() would call resetTaskAndStop() upon task completion.
             Log.d(TAG, "Cancel request filed successfully");
+            // Dismiss the notification as soon as possible as DynamicSystemManager.remove() may
+            // block.
+            stopForeground(STOP_FOREGROUND_REMOVE);
         } else {
             Log.e(TAG, "Trying to cancel installation while it's already completed.");
         }
@@ -322,8 +316,7 @@
         if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
             Log.e(TAG, "Trying to discard AOT while there is no complete installation");
             // Stop foreground state and dismiss stale notification.
-            stopForeground(STOP_FOREGROUND_REMOVE);
-            resetTaskAndStop();
+            resetTaskAndStop(true);
             return;
         }
 
@@ -331,8 +324,8 @@
                 getString(R.string.toast_dynsystem_discarded),
                 Toast.LENGTH_LONG).show();
 
-        resetTaskAndStop();
         postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+        resetTaskAndStop(true);
 
         mDynSystem.remove();
     }
@@ -412,12 +405,13 @@
     }
 
     private void resetTaskAndStop() {
-        mInstallTask = null;
+        resetTaskAndStop(/* removeNotification= */ false);
+    }
 
-        new Handler().postDelayed(() -> {
-            stopForeground(STOP_FOREGROUND_DETACH);
-            stopSelf();
-        }, 50);
+    private void resetTaskAndStop(boolean removeNotification) {
+        mInstallTask = null;
+        stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROUND_DETACH);
+        stopSelf();
     }
 
     private void prepareNotification() {
@@ -525,7 +519,7 @@
     private void postStatus(int status, int cause, Throwable detail) {
         String statusString;
         String causeString;
-        mKeepNotification = false;
+        boolean notifyOnNotificationBar = true;
 
         switch (status) {
             case STATUS_NOT_STARTED:
@@ -551,18 +545,16 @@
                 break;
             case CAUSE_INSTALL_CANCELLED:
                 causeString = "INSTALL_CANCELLED";
+                notifyOnNotificationBar = false;
                 break;
             case CAUSE_ERROR_IO:
                 causeString = "ERROR_IO";
-                mKeepNotification = true;
                 break;
             case CAUSE_ERROR_INVALID_URL:
                 causeString = "ERROR_INVALID_URL";
-                mKeepNotification = true;
                 break;
             case CAUSE_ERROR_EXCEPTION:
                 causeString = "ERROR_EXCEPTION";
-                mKeepNotification = true;
                 break;
             default:
                 causeString = "CAUSE_NOT_SPECIFIED";
@@ -571,16 +563,6 @@
 
         Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
 
-        boolean notifyOnNotificationBar = true;
-
-        if (status == STATUS_NOT_STARTED
-                && cause == CAUSE_INSTALL_CANCELLED
-                && mJustCancelledByUser) {
-            // if task is cancelled by user, do not notify them
-            notifyOnNotificationBar = false;
-            mJustCancelledByUser = false;
-        }
-
         if (notifyOnNotificationBar) {
             mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
         }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 4ef5e2b..59ea9f0 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,7 +320,7 @@
         }
     }
 
-    private void installScratch() throws IOException, InterruptedException {
+    private void installScratch() throws IOException {
         final long scratchSize = mDynSystem.suggestScratchSize();
         Thread thread = new Thread() {
             @Override
@@ -347,7 +347,11 @@
                 publishProgress(progress);
             }
 
-            Thread.sleep(100);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore the error.
+            }
         }
 
         if (mInstallationSession == null) {
@@ -361,7 +365,7 @@
         }
     }
 
-    private void installUserdata() throws IOException, InterruptedException {
+    private void installUserdata() throws IOException {
         Thread thread = new Thread(() -> {
             mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
         });
@@ -383,7 +387,11 @@
                 publishProgress(progress);
             }
 
-            Thread.sleep(100);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore the error.
+            }
         }
 
         if (mInstallationSession == null) {
@@ -397,8 +405,7 @@
         }
     }
 
-    private void installImages()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installImages() throws IOException, ImageValidationException {
         if (mStream != null) {
             if (mIsZip) {
                 installStreamingZipUpdate();
@@ -410,14 +417,12 @@
         }
     }
 
-    private void installStreamingGzUpdate()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installStreamingGzUpdate() throws IOException, ImageValidationException {
         Log.d(TAG, "To install a streaming GZ update");
         installImage("system", mSystemSize, new GZIPInputStream(mStream));
     }
 
-    private void installStreamingZipUpdate()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installStreamingZipUpdate() throws IOException, ImageValidationException {
         Log.d(TAG, "To install a streaming ZIP update");
 
         ZipInputStream zis = new ZipInputStream(mStream);
@@ -432,8 +437,7 @@
         }
     }
 
-    private void installLocalZipUpdate()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installLocalZipUpdate() throws IOException, ImageValidationException {
         Log.d(TAG, "To install a local ZIP update");
 
         Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -449,7 +453,7 @@
     }
 
     private boolean installImageFromAnEntry(ZipEntry entry, InputStream is)
-            throws IOException, InterruptedException, ImageValidationException {
+            throws IOException, ImageValidationException {
         String name = entry.getName();
 
         Log.d(TAG, "ZipEntry: " + name);
@@ -473,7 +477,7 @@
     }
 
     private void installImage(String partitionName, long uncompressedSize, InputStream is)
-            throws IOException, InterruptedException, ImageValidationException {
+            throws IOException, ImageValidationException {
 
         SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
 
@@ -504,7 +508,11 @@
                 return;
             }
 
-            Thread.sleep(100);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore the error.
+            }
         }
 
         if (mInstallationSession == null) {
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
index 781bfcd..1ccf417 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
@@ -51,6 +51,7 @@
 
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
+        setSingleLineTitle(true);
         super.onBindViewHolder(holder);
         final View switchView = holder.findViewById(android.R.id.switch_widget);
         if (switchView != null) {
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1a67b5e..0ee44f8 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rooi-groen)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (blou-geel)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Kleurregstelling"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Verstel hoe kleure op jou toestel vertoon. Dit kan nuttig wees wanneer jy:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Kleure meer akkuraat wil sien&lt;/li&gt; &lt;li&gt; Kleure wil verwyder om jou te help fokus&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Foonluidspreker"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string>
     <string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 97abce1..9fb676f 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ፕሮታኖማሊ (ቀይ-አረንጓዴ)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ትራይታኖማሊ (ሰማያዊ-ቢጫ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"የቀለም ማስተካከያ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ቀለሞች በመሣሪያዎ ላይ እንዴት እንደሚታዩ ያስተካክሉ። የሚከተሉትን ለማድረግ በሚፈልጉበት ጊዜ ይህ ጠቃሚ ሊሆን ይችላል፦&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ቀለሞችን የበለጠ ትክክለኛ በሆነ መልኩ ለመመልከት&lt;/li&gt; &lt;li&gt; ትኩረት ለማድረግ እንዲረዳዎ ቀለሞችን ለማስወገድ&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"የስልክ ድምጽ ማጉያ"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string>
     <string name="help_label" msgid="3528360748637781274">"እገዛ እና ግብረመልስ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f9e12c7..cdb418a 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"غطش الأحمر (الأحمر والأخضر)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"غمش الأزرق (الأزرق والأصفر)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"تصحيح الألوان"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏يمكنك تعديل كيفية عرض الألوان على جهازك. يساعدك هذا الخيار عندما تريد تنفيذ ما يلي:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; عرض الألوان بمزيد من الدقة&lt;/li&gt; &lt;li&gt; إزالة الألوان لمساعدتك على التركيز&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا"</string>
@@ -520,8 +519,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
     <string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات والآراء"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index efd813a..442a19d 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্ৰ’টানোমালি (ৰঙা-সেউজীয়া)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্ৰাইটান\'মেলী (নীলা-হালধীয়া)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ৰং শুধৰণী"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"আপোনাৰ ডিভাইচত ৰংবোৰ কেনেকৈ প্ৰদৰ্শিত হয় সেয়া মিলাওক। এইটো আপুনি এই কাৰ্য কৰিবলৈ বিচাৰিলে সহায়ক হ\'ব পাৰে:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ৰং অধিক সঠিককৈ চাবলৈ বিচৰা&lt;/li&gt; &lt;li&gt; আপোনাক মনোযোগ দিয়াত সহায় কৰিবলৈ ৰং আঁতৰোৱা&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফ’নৰ স্পীকাৰ"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string>
     <string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7a5aec4..950a5ae 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Пратанамалія (чырвоны-зялёны)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Трытанамалія (сіні-жоўты)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Карэкцыя колеру"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Наладзьце адлюстраванне колераў на экране прылады. Гэта налада можа быць карыснай, калі вы захочаце:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; бачыць колеры больш дакладна;&lt;/li&gt; &lt;li&gt; выдаліць колеры, якія перашкаджаюць вам сканцэнтравацца&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Зараду хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Дынамік тэлефона"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string>
     <string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 675d179..4cc9abf 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"প্রোটানোম্যালি (লাল-সবুজ)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ট্রিট্যানোম্যালি (নীল-হলুদ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"রঙ সংশোধন"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"আপনার ডিভাইসে রঙগুলি কেমন দেখাবে তা অ্যাডজাস্ট করুন। যেক্ষেত্রে এটি আপনাকে সহায়তা করতে পারে:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; আরও নির্ভুলভাবে রঙ দেখতে&lt;/li&gt; &lt;li&gt; রঙ সরিয়ে দিলে ফোকাস করতে সহায়তা করবে&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফোনের স্পিকার"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
     <string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 97083ad..e4bc5a1 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno-zeleno)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo-žuto)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Ispravka boje"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Podešavanje načina na koji se boje prikazuju na uređaju. To može biti korisno kada želite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; tačnije prikazati boje&lt;/li&gt; &lt;li&gt; ukloniti boje da se lakše fokusirate&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
     <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 4889220..fa441a2 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermell-verd)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (blau-groc)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correcció de color"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta com es mostren els colors al teu dispositiu. Això pot ser útil quan vulguis:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Veure els colors amb més claredat.&lt;/li&gt; &lt;li&gt; Suprimir colors per poder enfocar més fàcilment.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altaveu del telèfon"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Desactiva el dispositiu i torna\'l a activar."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string>
     <string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index e857e60..717b681 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomálie (červená a zelená)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomálie (modrá a žlutá)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekce barev"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Upravte zobrazování barev na svém zařízení. To se může hodit, když chcete:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Aby se barvy zobrazovaly přesněji&lt;/li&gt; &lt;li&gt; Odstranit barvy, abyste se mohli lépe soustředit&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dokud tuto funkci nevypnete"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefonu"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string>
     <string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 53e21ba..6b17db1 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopi (rød-grøn)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopi (blå-gul)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korriger farver"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Juster, hvordan farverne vises på skærmen. Dette kan være nyttigt, når du vil:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Se farver mere nøjagtigt&lt;/li&gt; &lt;li&gt; Fjerne farver, så du bedre kan fokusere&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens højttaler"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string>
     <string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string>
@@ -541,7 +539,7 @@
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"Begrænset profil"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"Vil du tilføje en ny bruger?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enhed med andre ved at oprette ekstra brugere. Hver bruger har sit personlige område, som kan tilpasses med apps, baggrund osv. Brugerne kan også justere enhedsindstillinger, som for eksempel Wi-Fi, som påvirker alle.\n\nNår du tilføjer en ny bruger, skal vedkommende konfigurere sit område.\n\nAlle brugere kan opdatere apps for alle andre brugere. Indstillinger og tjenester for hjælpefunktioner overføres muligvis ikke til den nye bruger."</string>
-    <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit rum.\n\nEnhver bruger kan opdatere apps for alle andre brugere."</string>
+    <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du tilføjer en ny bruger, skal personen konfigurere sit rum.\n\nAlle brugere kan opdatere apps for alle de andre brugere."</string>
     <string name="user_setup_dialog_title" msgid="8037342066381939995">"Vil du konfigurere brugeren nu?"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"Sørg for, at brugeren har mulighed for at tage enheden og konfigurere sit eget rum"</string>
     <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Vil du oprette en profil nu?"</string>
@@ -557,7 +555,7 @@
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
-    <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index a4a976f..bb11ed1 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (Rot-Grün-Sehschwäche)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (Blau-Gelb-Sehschwäche)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Farbkorrektur"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Hier kannst du anpassen, wie Farben auf deinem Gerät dargestellt werden sollen. Das kann in folgenden Fällen hilfreich sein:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Wenn Farben genauer dargestellt werden sollen&lt;/li&gt; &lt;li&gt; Wenn du Farben entfernen möchtest, um dich besser konzentrieren zu können&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Smartphone-Lautsprecher"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus &amp; und wieder ein."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
     <string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 71cf5cb..fb294c8 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta cómo se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieres:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver colores con más exactitud&lt;/li&gt; &lt;li&gt; Quitar colores para mejorar tu concentración&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
     <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a7d7b21..2c5eff2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajusta el modo en que se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieras hacer lo siguiente:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver los colores con más precisión&lt;/li&gt; &lt;li&gt; Eliminar colores para ayudarte a mantener la concentración&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
     <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index add25f8..116faad 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaalia (punane-roheline)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaalia (sinine-kollane)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värvide korrigeerimine"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Kohandage seadmes värvide kuvamist. Sellest võib olla kasu, kui soovite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; värve täpsemalt näha;&lt;/li&gt; &lt;li&gt; värve eemaldada, et paremini keskenduda.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoni kõlar"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string>
     <string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index c95d157..bfb4efc 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopia (urdin-horia)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Doitu nola bistaratzen diren koloreak gailuan. Kasu hauetan izan daiteke lagungarria:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Koloreak zehatzago ikusi nahi dituzunean.&lt;/li&gt; &lt;li&gt; Hobeto fokuratzeko, koloreak kendu nahi dituzunean.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonoaren bozgorailua"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazoren bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
     <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 631ff5e..f3018ba 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"قرمزدشواربینی (قرمز-سبز)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"آبی‌دشواربینی (آبی-زرد)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"تصحیح رنگ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏نحوه نمایش رنگ‌ها را در دستگاهتان تنظیم می‌کند. این ویژگی می‌تواند در موارد زیر مفید باشد:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; وقتی می‌خواهید رنگ‌ها را با دقت بیشتری ببینید&lt;/li&gt; &lt;li&gt; وقتی می‌خواهید رنگ‌ها را حذف کنید تا تمرکز بیشتری داشته باشید"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانی‌که آن را خاموش کنید"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"هم‌اکنون"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"بلندگوی تلفن"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string>
     <string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7591c91..7089937 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (puna-vihersokeus)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (sini-keltasokeus)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värinkorjaus"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Muuta värien näkymistä laitteellasi. Tästä voi olla hyötyä, kun haluat&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; nähdä värit tarkemmin&lt;/li&gt; &lt;li&gt; poistaa värejä voidaksesi keskittyä paremmin&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Puhelimen kaiutin"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string>
     <string name="help_label" msgid="3528360748637781274">"Ohje ja palaute"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a390257..ac8adde 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu/jaune)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustez l\'affichage des couleurs sur votre appareil. Ce paramètre peut être utile si vous voulez :&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Mieux distinguer les couleurs&lt;/li&gt; &lt;li&gt; Enlever les couleurs pour vous aider à vous concentrer&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
     <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8cf8c6e..e4e4293 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu-jaune)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustez l\'affichage des couleurs sur votre appareil. Cette option peut vous être utile pour :&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; accentuer la précision des couleurs ;&lt;/li&gt; &lt;li&gt; supprimer les couleurs pour mieux vous concentrer.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string>
     <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index c1ba51d..062b7b3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (vermello-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarelo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección da cor"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Axusta a maneira en que se mostran as cores no teu dispositivo. Esta opción pode resultarche útil se queres:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver mellor as cores&lt;/li&gt; &lt;li&gt; Quitar as cores para concentrarte mellor&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altofalante do teléfono"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
     <string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 94d83e0..174d0a1 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"પ્રોટેનોમલી (લાલ-લીલો)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ટ્રાઇટેનોમલી(વાદળી-પીળો)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"રંગ સુધારણા"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"તમારા ડિવાઇસ પર રંગો કેવી રીતે બતાવવામાં આવે તેની ગોઠવણી કરો. આ ત્યારે સહાયરૂપ થઈ શકે છે જ્યારે તમારે:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; રંગો વધુ યોગ્ય રીતે જોવા હોય&lt;/li&gt; &lt;li&gt; તમને ફોકસ કરવામાં સહાયતા રહે તે માટે રંગો કાઢી નાખવા હોય&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ફોન સ્પીકર"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string>
     <string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index e99d4cb..3d06baa 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (crveno – zeleno)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (plavo – žuto)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcija boje"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Prilagodite način prikazivanja boja na svojem uređaju. To može biti korisno kad želite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; vidjeti boje točnije&lt;/li&gt; &lt;li&gt; ukloniti boje kako biste se lakše usredotočili&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string>
     <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 503ee60..546a038 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (piros– zöld)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (kék–sárga)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Színkorrekció"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Korrigálhatja a színek megjelenítését az eszközén. Ez a következő esetekben lehet hasznos:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ha pontosabb színeket szeretne látni;&lt;/li&gt; &lt;li&gt; ha szeretné eltávolítani a színeket, hogy jobban tudjon koncentrálni.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hangszórója"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string>
     <string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 3686dd1..7c0963d 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Պրոտանոմալիա (կարմիր-կանաչ)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Տրիտանոմալիա (կապույտ-դեղին)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Գունաշտկում"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Կարգավորեք գույների ցուցադրումը ձեր սարքում։ Դա կարող է օգտակար լինել, երբ դուք ուզում եք՝&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ավելի հստակ տեսնել գույները&lt;/li&gt; &lt;li&gt; Հեռացնել գույները՝ կենտրոնանալու համար&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև չանջատեք"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Հեռախոսի բարձրախոս"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string>
     <string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 035be7d..2f8bb46 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (merah-hijau)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (biru-kuning)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koreksi warna"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Sesuaikan cara warna ditampilkan di perangkat Anda. Ini dapat bermanfaat saat Anda ingin:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Melihat warna dengan lebih akurat&lt;/li&gt; &lt;li&gt; Menghapus warna untuk membantu Anda fokus&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ponsel"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat &amp; aktifkan kembali"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string>
     <string name="help_label" msgid="3528360748637781274">"Bantuan &amp; masukan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 718b5be..ea29258 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Litblinda (rauðgræn)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Litblinda (blágul)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Litaleiðrétting"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Stilltu litabirtingu í tækinu þínu. Þetta getur gagnast þegar þú vilt:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Sjá liti skýrar&lt;/li&gt; &lt;li&gt; Fjarlægja liti til að fókusa betur&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Símahátalari"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string>
     <string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index d2176d9..5dd7a52 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalìa (rosso-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalìa (blu-giallo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correzione del colore"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Regola la modalità di visualizzazione dei colori sul tuo dispositivo. Può essere utile se vuoi:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Vedere i colori con più precisione&lt;/li&gt; &lt;li&gt; Rimuovere colori per mettere a fuoco più facilmente&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlante telefono"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
     <string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1357cae..20d1e1b 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"פרוטנומליה (אדום-ירוק)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"טריטנומליה (כחול-צהוב)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"תיקון צבע"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏ניתן לשנות את האופן שבו צבעים מוצגים במכשיר. שינוי כזה עשוי לעזור:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; להבחין בצבעים בצורה יותר מדויקת&lt;/li&gt; &lt;li&gt; להסיר צבעים מסוימים כדי להתמקד&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"רמקול של טלפון"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string>
     <string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 8bb6ba8..37db15c 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"პროტოანომალია (წითელი-მწვანე)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ტრიტანომალია (ლურჯი-ყვითელი)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ფერის კორექცია"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"დააკორექტირეთ, როგორ გამოჩნდება ფერები თქვენს მოწყობილობაზე. ეს შეიძლება დაგეხმაროთ, როდესაც გსურთ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ფერების მეტი სიზუსტით დანახვა&lt;/li&gt; &lt;li&gt; ფერების მოცილება, რომ უკეთ კონცენტრირდეთ&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ტელეფონის დინამიკი"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string>
     <string name="help_label" msgid="3528360748637781274">"დახმარება და გამოხმაურება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 6a2c21e..878966d 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (қызыл-жасыл)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (көк-сары)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Түсті түзету"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Құрылғыңызда түстер қалай көрсетілетінін реттеңіз. Бұл мыналар үшін пайдалы болуы мүмкін:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; түстерді анығырақ көру&lt;/li&gt; &lt;li&gt; зейініңізді жақсарту үшін түстерді өшіру&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string>
@@ -503,7 +502,7 @@
     <string name="cancel" msgid="5665114069455378395">"Бас тарту"</string>
     <string name="okay" msgid="949938843324579502">"Жарайды"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
-    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Мазаламау\" режимін қосу"</string>
+    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
     <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ешқашан"</string>
     <string name="zen_interruption_level_priority" msgid="5392140786447823299">"Маңыздылары ғана"</string>
     <string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефон динамигі"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string>
     <string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string>
@@ -557,8 +555,8 @@
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
-    <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index f4cece4..708879a 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -556,7 +556,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើត​អ្នកប្រើប្រាស់ថ្មី…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះ​ហៅក្រៅ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"លុប​​​ភ្ញៀវ"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើស​រូបភាព"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a844ee6..daf7b6f 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ಪ್ರೊಟನೋಮಲಿ (ಕೆಂಪು-ಹಸಿರು)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ಟ್ರಿಟನೋಮಲಿ (ನೀಲಿ-ಹಳದಿ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಬಣ್ಣಗಳು ಹೇಗೆ ಡಿಸ್‌ಪ್ಲೇ ಆಗುತ್ತವೆ ಎಂಬುದನ್ನು ಹೊಂದಿಸಿ. ನೀವು ಬಣ್ಣಗಳನ್ನು ಹೆಚ್ಚು ನಿಖರವಾಗಿ ನೋಡಲು ಬಯಸಿದಾಗ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ಇದು ಸಹಾಯಕವಾಗಿರುತ್ತದೆ&lt;/li&gt; &lt;li&gt; ನಿಮಗೆ ಗಮನಹರಿಸಲು ಸಹಾಯ ಮಾಡಲು ಬಣ್ಣಗಳನ್ನು ತೆಗೆದುಹಾಕಿ&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
     <string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 4ca6567..834200d 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (кызыл-жашыл)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (көк-сары)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Түсүн тууралоо"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Түзмөгүңүздө түстөр кантип көрүнөрүн тууралаңыз. Бул төмөнкү учурларда пайдалуу болот:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Түстөрдү даана көрүү&lt;/li&gt; &lt;li&gt; Ынтаа коюу үчүн түстөрдү өчүрүү&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефондун динамиги"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string>
     <string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 176c80f..0dcf1627 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ສີ​ແດງ​-ສີ​ຂຽວ​)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ສີ​ຟ້າ​-ສີ​ເຫຼືອງ​)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ການ​ປັບ​ແຕ່ງ​ສີ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ປັບແກ້ການສະແດງສີຢູ່ອຸປະກອນຂອງທ່ານ. ນີ້ອາດມີປະໂຫຍດໃນເວລາທີ່ທ່ານຕ້ອງການ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ເບິ່ງເຫັນສີໄດ້ຖືກຕ້ອງຍິ່ງຂຶ້ນ&lt;/li&gt; &lt;li&gt; ລຶບສີອອກເພື່ອຊ່ວຍໃຫ້ທ່ານມີສະມາທິ&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ລຳໂພງໂທລະສັບ"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string>
     <string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b0ab8f4..c3f9202 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (raudona, žalia)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (mėlyna, geltona)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Spalvų taisymas"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Koreguokite, kaip spalvos rodomos jūsų įrenginyje. Tai gali būti naudinga, kai norite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; matyti tikslesnes spalvas;&lt;/li&gt; &lt;li&gt; pašalinti spalvas, kad būtų lengviau susitelkti.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefono garsiakalbis"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string>
     <string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2633f13..5090df0 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomālija (sarkans/zaļš)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomālija (zils/dzeltens)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Krāsu korekcija"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Pielāgojiet krāsu attēlojumu jūsu ierīcē. Izmantojiet šo funkciju, lai:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; skatītu precīzāku krāsu attēlojumu;&lt;/li&gt; &lt;li&gt; noņemtu krāsas, kad jāpievēršas kādam uzdevumam.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> — <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Tālruņa skaļrunis"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string>
     <string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index e4173d0..c4890d4 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалија (слепило за црвена и зелена)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалија (слепило за сина и жолта)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекција на бои"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Приспособете го приказот на боите на уредот. Ова е корисно кога сакате:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; да гледате попрецизни бои&lt;/li&gt; &lt;li&gt; да отстраните бои за подобра концентрација&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Неодамнешни"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефонски звучник"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
     <string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 3cfe479..829d474 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"പ്രോട്ടാനോമലി (ചുവപ്പ്-പച്ച)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ട്രിട്ടാനോമലി (നീല-മഞ്ഞ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"നിറം ക്രമീകരിക്കൽ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"നിങ്ങളുടെ ഉപകരണത്തിൽ നിറങ്ങൾ എങ്ങനെ പ്രദർശിപ്പിക്കണമെന്ന് ക്രമീകരിക്കുക. ഇനിപ്പറയുന്ന കാര്യങ്ങൾ ചെയ്യാൻ ആഗ്രഹിക്കുമ്പോൾ ഇത് സഹായകരമാകും:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; നിറങ്ങൾ കൂടുതൽ കൃത്യമായി കാണാൻ&lt;/li&gt; &lt;li&gt; ഫോക്കസ് ചെയ്യാൻ നിങ്ങളെ സഹായിക്കുന്നതിന് നിറങ്ങൾ നീക്കം ചെയ്യാൻ&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ഫോൺ സ്‌പീക്കർ"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്‌റ്റ് ചെയ്യുന്നതിൽ പ്രശ്‌നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string>
     <string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്‌ബാക്കും"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index f8d9089..e9fe1dd 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"क्षीण रक्तवर्णांधता (लाल-हिरवा)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"रंग दृष्टी कमतरता (निळा-पिवळा)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रंग सुधारणा"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"तुमच्या डिव्हाइसवर रंग कसे प्रदर्शित केले जातात ते अ‍ॅडजस्ट करा. तुम्हाला &lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt;अधिक स्पष्टपणे रंग पाहणे &lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt; तुम्हाला फोकस करण्यात मदत करण्यासाठी रंग काढून टाकणे&lt;/br&gt;&lt;br&gt; &lt;/br&gt;&lt;br&gt; हे करायचे असते तेव्हा उपयुक्त असू शकते."</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनचा स्पीकर"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्‍ट करण्‍यात समस्‍या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string>
     <string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f10e539..d80242ef 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (အနီ-အစိမ်း)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (အပြာ-အဝါ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင်ပြင်ဆင်မှု"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"သင့်စက်ပစ္စည်းတွင် အရောင်များပြသပုံကို ချိန်ညှိပါ။ ၎င်းက အောက်ပါတို့တွင် အသုံးဝင်နိုင်သည်-&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; အရောင်များကို ပိုမိုတိကျစွာ မြင်လိုခြင်း&lt;/li&gt; &lt;li&gt; သင်အာရုံစိုက်နိုင်ရန် အရောင်များကို ဖယ်ရှားခြင်း&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ဖုန်းစပီကာ"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string>
     <string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 9fb68d0..7b49b40 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (rød-grønn)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (blå-gul)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Fargekorrigering"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Juster hvordan farger vises på enheten. Dette kan være nyttig når du vil&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; se farger mer nøyaktig&lt;/li&gt; &lt;li&gt; fjerne farger for å gjøre det enklere å fokusere&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhøyttaler"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string>
     <string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4c847cf..4d380a5 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"प्रोटानेमली (रातो, हरियो)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ट्रिटानोमेली (निलो-पंहेलो)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रङ्ग सुधार"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"तपाईंको यन्त्रमा रङहरू कस्ता देखिन्छन् भन्ने कुरा मिलाउनुहोस्। यो सुविधा निम्न अवस्थामा उपयोगी हुन सक्छ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; तपाईं अझ सटीक रूपमा रङहरू देख्न चाहनुहुन्छ भने&lt;/li&gt; &lt;li&gt; तपाईं कुनै कुरामा ध्यान केन्द्रित गर्न रङहरू हटाउन चाहनुहुन्छ भने&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनको स्पिकर"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि सक्रिय गर्नुहोस्"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string>
     <string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string>
@@ -541,7 +539,7 @@
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबन्धित प्रोफाइल"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"नयाँ प्रयोगकर्ता थप्ने हो?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"तपाईं थप प्रयोगकर्ताहरू सिर्जना गरेर ती प्रयोगकर्तालाई यो यन्त्र प्रयोग गर्न दिन सक्नुहुन्छ। हरेक प्रयोगकर्ताको आफ्नै ठाउँ हुन्छ। उनीहरू यो ठाउँमा आफ्नै एप, वालपेपर आदिका लागि प्रयोग गर्न सक्छन्। उनीहरू सबैजनालाई असर पार्ने Wi-Fi जस्ता यन्त्रका सेटिङहरू पनि परिवर्तन गर्न सक्छन्।\n\nतपाईंले नयाँ प्रयोगकर्ता थप्दा उक्त व्यक्तिले आफ्नो ठाउँ सेटअप गर्नु पर्ने हुन्छ।\n\nसबै प्रयोगकर्ता अन्य सबै प्रयोगकर्ताले प्रयोग गर्ने एपहरू अद्यावधिक गर्न सक्छन्। तर पहुँचसम्बन्धी सेटिङ तथा सेवाहरू नयाँ प्रयोगकर्तामा नसर्न सक्छ।"</string>
-    <string name="user_add_user_message_short" msgid="3295959985795716166">"जब तपाईंले नयाँ प्रयोगकर्ता थप्नुहुन्छ, त्यो व्यक्तिले आफ्नो ठाउँ सेट गर्न आवश्यक छ।\n\nकुनै पनि प्रयोगकर्ताले सबै अन्य प्रयोगकर्ताहरूका लागि एपहरू अद्यावधिक गर्न सक्छन्।"</string>
+    <string name="user_add_user_message_short" msgid="3295959985795716166">"तपाईंले नयाँ प्रयोगकर्ता थप्नुभयो भने ती प्रयोगकर्ताले आफ्नो स्पेस सेट गर्नु पर्ने हुन्छ।\n\nसबै प्रयोगकर्ताले अरू प्रयोगकर्ताका एपहरू अपडेट गर्न सक्छन्।"</string>
     <string name="user_setup_dialog_title" msgid="8037342066381939995">"अहिले प्रयोगकर्ता सेटअप गर्ने हो?"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"यी व्यक्ति यन्त्र यो यन्त्र चलाउन र आफ्नो ठाउँ सेट गर्न उपलब्ध छन् भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
     <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"अहिले प्रोफाइल सेटअप गर्ने हो?"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 4cb755b..518aaa9 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -371,7 +371,7 @@
     <string name="app_process_limit_title" msgid="8361367869453043007">"Achtergrondproceslimiet"</string>
     <string name="show_all_anrs" msgid="9160563836616468726">"ANR\'s op de achtergrond"</string>
     <string name="show_all_anrs_summary" msgid="8562788834431971392">"Dialoogvenster \'App reageert niet\' weergeven voor achtergrond-apps"</string>
-    <string name="show_notification_channel_warnings" msgid="3448282400127597331">"Kanaalwaarschuwingen voor meldingen weergeven"</string>
+    <string name="show_notification_channel_warnings" msgid="3448282400127597331">"Kanaalwaarschuwingen voor meldingen tonen"</string>
     <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Geeft een waarschuwing op het scherm weer wanneer een app een melding post zonder geldig kanaal"</string>
     <string name="force_allow_on_external" msgid="9187902444231637880">"Toestaan van apps op externe opslag afdwingen"</string>
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Hiermee komt elke app in aanmerking voor schrijven naar externe opslag, ongeacht de manifestwaarden"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index b7c93e5..b0fff21 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ପ୍ରୋଟାନୋମାଲି (ଲାଲ୍‌-ସବୁଜ)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ନୀଳ-ହଳଦିଆ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ଆପଣଙ୍କ ଡିଭାଇସରେ ରଙ୍ଗଗୁଡ଼ିକ କିପରି ଡିସପ୍ଲେ ହୁଏ ତାହା ଆଡଜଷ୍ଟ କରନ୍ତୁ। ଆପଣ ଏହା କରିବାକୁ ଚାହିଁଲେ ଏହା ଉପଯୋଗୀ ହୋଇପାରେ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ଆହୁରି ସଠିକ୍ ଭାବେ ରଙ୍ଗଗୁଡ଼ିକୁ ଦେଖିବା&lt;/li&gt; &lt;li&gt; ଆପଣଙ୍କୁ ଫୋକସ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ ରଙ୍ଗଗୁଡ଼ିକୁ କାଢ଼ିବା&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍‌ରାଇଡ୍‌ କରାଯାଇଛି"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ଫୋନ୍ ସ୍ପିକର୍"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
     <string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 896a100..71e2dba 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ਲਾਲ-ਹਰਾ)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ਨੀਲਾ-ਪੀਲਾ)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ਰੰਗ ਸੁਧਾਈ"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਰੰਗਾਂ ਨੂੰ ਦਿਖਾਉਣ ਦੇ ਤਰੀਕੇ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ। ਇਹ ਉਦੋਂ ਲਾਹੇਵੰਦ ਹੋ ਸਕਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਇਹ ਕਰਨਾ ਚਾਹੋਗੇ:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ਰੰਗਾਂ ਨੂੰ ਹੋਰ ਸਟੀਕਤਾ ਨਾਲ ਦੇਖਣਾ&lt;/li&gt; &lt;li&gt; ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਲਈ ਰੰਗਾਂ ਨੂੰ ਹਟਾਉਣਾ&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string>
     <string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 663f3fc..c8d7287 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (czerwony-zielony)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (niebieski-żółty)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korekcja kolorów"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Dostosuj sposób wyświetlania kolorów na ekranie urządzenia. Może to być pomocne, gdy chcesz:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; dokładniej widzieć kolory;,&lt;/li&gt; &lt;li&gt; usunąć wybrane kolory, aby móc skuteczniej się skupić.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Głośnik telefonu"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
     <string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 8eab928..1d6624d 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção de cor"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste as cores exibidas no seu dispositivo. Esta opção pode ser útil quando você quer:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ver cores com mais precisão;&lt;/li&gt; &lt;li&gt; remover cores para se concentrar melhor.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
     <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f28d70a..66d4b23 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção da cor"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste a visualização das cores no dispositivo. Isto pode ser útil quando pretender:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver cores com maior precisão&lt;/li&gt; &lt;li&gt; Remover cores para melhorar a sua concentração&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altifalante do telemóvel"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
     <string name="help_label" msgid="3528360748637781274">"Ajuda e comentários"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 8eab928..1d6624d 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalia (vermelho-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (azul-amarelo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correção de cor"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajuste as cores exibidas no seu dispositivo. Esta opção pode ser útil quando você quer:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; ver cores com mais precisão;&lt;/li&gt; &lt;li&gt; remover cores para se concentrar melhor.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
     <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0a4e462..3e77ab6 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (roșu-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (albastru-galben)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corecția culorii"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ajustați modul în care se afișează culorile pe dispozitiv. Acest lucru poate fi util când doriți să:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; vedeți culorile mai bine&lt;/li&gt; &lt;li&gt; eliminați culorile pentru a vă ajuta să vă concentrați&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -517,8 +516,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivați"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Difuzorul telefonului"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Opriți și reporniți dispozitivul."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string>
     <string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index b030715..b342287 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (красный/зеленый)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (синий/желтый)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Коррекция цвета"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Настройте цветопередачу на экране устройства. Эта функция может помочь:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; сделать цвета более четкими;&lt;/li&gt; &lt;li&gt; убрать цвета, чтобы вам проще было сфокусироваться.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"Уровень заряда – <xliff:g id="PERCENTAGE">%1$s</xliff:g>. <xliff:g id="TIME_STRING">%2$s</xliff:g>."</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Встроенный динамик"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string>
     <string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string>
@@ -559,7 +557,7 @@
     <string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
-    <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index d9d5351..b24ac8e 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (červená a zelená)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (modrá a žltá)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Úprava farieb"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Upravte si zobrazovanie farieb v zariadení. Môže to byť užitočné, ak chcete:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; zobraziť presnejšie viac farieb;y&lt;/li&gt; &lt;li&gt; odstrániť farby, aby ste sa mohli lepšie sústrediť.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefónu"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string>
     <string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 81b1b60..626402a 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (rdeča – zelena)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (modra – rumena)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Popravljanje barv"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Prilagodite prikaz barv v napravi. To je uporabno, ko želite:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; videti bolj prave barve;&lt;/li&gt; &lt;li&gt; odstraniti barve, da se lažje osredotočite.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvočnik telefona"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string>
     <string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index b71e51f..4f22348 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (e kuqe - e gjelbër)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (e kaltër - e verdhë)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korrigjimi i ngjyrës"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Rregullo mënyrën se si ngjyrat afishohen në pajisjen tënde. Kjo mund të jetë e dobishme kur dëshiron që:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; T\'i shikosh ngjyrat me më shumë saktësi&lt;/li&gt; &lt;li&gt; T\'i heqësh ngjyrat për të të ndihmuar të fokusohesh&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Deri sa ta çaktivizosh"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlanti i telefonit"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
     <string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 2ec2ded..069b6a5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (rött-grönt)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (blått-gult)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Färgkorrigering"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Ändra hur färger visas på enheten. Det kan vara ett bra hjälpmedel när du vill&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; att färger ska visas mer exakt&lt;/li&gt; &lt;li&gt; ta bort färger för att fokusera bättre&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens högtalare"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string>
     <string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 410a4aa..0fb620c 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -555,7 +555,7 @@
     <string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
-    <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 14eb25d..3106a69 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"வண்ணத்திருத்தம்"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"சாதனத்தில் வண்ணங்கள் காண்பிக்கப்படும் விதத்தைச் சரிசெய்யலாம். இதன் மூலம் நீங்கள் விரும்பும்போதெல்லாம்:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; வண்ணங்களை மிகத் தெளிவாகப் பார்க்கலாம்&lt;/li&gt; &lt;li&gt; கவனம் சிதறாமல் இருக்க வண்ணங்களை நீக்கலாம்&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"மொபைல் ஸ்பீக்கர்"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string>
     <string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index e652ab7..1f78c0c 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"కలర్ సరిచేయడం"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"మీ పరికరంపై రంగులు కనిపించే విధానాన్ని అడ్జస్ట్ చేయండి. మీకు కావలసినప్పుడు, ఇది సహాయకరంగా ఉంటుంది:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; మరింత ఖచ్చితంగా రంగులను చూడండి&lt;/li&gt; &lt;li&gt; మీరు ఫోకస్ చేయడంలో సహాయపడటానికి రంగులను తీసివేయండి&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్‌ చేసే వరకు"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
     <string name="help_label" msgid="3528360748637781274">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
@@ -557,7 +555,7 @@
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్‌ను క్రియేట్ చేస్తోంది…"</string>
     <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
-    <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్‌ను జోడించండి"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 5fbf5d4..ef516c4 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (kırmızı-yeşil)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (mavi-sarı)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Renk düzeltme"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Renklerin cihazınızda nasıl görüntüleneceğini düzenleyin Bu, şunları yapmak istediğinizde kullanışlı olur:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Renkleri daha doğru görmek&lt;/li&gt; &lt;li&gt; Odaklanmanıza yardımcı olması için renkleri kaldırmak&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hoparlörü"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string>
     <string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 4511a4b..81528ad 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалія (червоний – зелений)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалія (синій – жовтий)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекція кольору"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Налаштуйте відтворення кольорів на екрані пристрою. Це може бути корисно, якщо ви хочете:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; точніше відтворювати кольори;&lt;/li&gt; &lt;li&gt; вилучити кольори, щоб зосередитися на головному.&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -518,8 +517,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Динамік телефона"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
     <string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index e61d281..798c885 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"‏Protanomaly (سرخ سبز)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"‏Tritanomaly (نیلا پیلا)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"رنگ کی اصلاح"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"‏آپ کے آلے پر رنگوں کے ڈسپلے ہونے کے طریقے کو ایڈجسٹ کریں۔ یہ خصوصیت درج ذیل کے لیے مددگار ثابت ہو سکتی ہے:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; جب آپ رنگوں کو مزید درست طریقے سے دیکھنا چاہیں &lt;/li&gt; &lt;li&gt; فوکس کرنے میں مدد کے لیے رنگوں کو ہٹادیں&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"فون اسپیکر"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string>
     <string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 0730c6c..6efc0be 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -556,7 +556,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nik"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7d1bde3..67923c5 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"红色弱视(红绿不分)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"蓝色弱视(蓝黄不分)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"调整设备上的颜色显示方式。此设置适用于以下情况:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;想要更准确地查看颜色&lt;/li&gt; &lt;li&gt;想要去除颜色,以便集中注意力&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手机扬声器"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string>
     <string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 06b5cd0..bf69b31 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -424,7 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"紅色弱視 (紅綠)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"藍色弱視 (藍黃)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"調整裝置顯示顏色的方式。這項設定適用於以下情況:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; 想讓裝置更準確地顯示顏色&lt;/li&gt; &lt;li&gt; 移除顏色以提高專注力&lt;/li&gt; &lt;/ol&gt;"</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"調整裝置顯示顏色嘅方式。呢項設定喺以下情況適用:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; 想令裝置更加準確咁顯示顏色&lt;/li&gt; &lt;li&gt; 移除顏色嚟提高專注力&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -515,7 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"直至您關閉為止"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string>
-    <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string>
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string>
     <string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 2297cbe..7b0274a 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -424,8 +424,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"I-Protanomaly (bomvu-luhlaza)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"I-Tritanomaly (luhlaza okwesibhakabhaka-phuzi)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Ukulungiswa kombala"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (8625527799885140826) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="8625527799885140826">"Lungisa indlela imibala eboniswa ngayo kudivayisi yakkho. Lokhu kungaba usizo lapho ufuna:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ukubona imibala ngokunembilie&lt;/li&gt; &lt;li&gt; Ukususa imibala ukuze ugxile&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele"</string>
@@ -516,8 +515,7 @@
     <string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string>
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Isipikha sefoni"</string>
-    <!-- no translation found for media_transfer_this_phone (7194341457812151531) -->
-    <skip />
+    <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string>
     <string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 8fd1910..129aca4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -386,7 +386,6 @@
                 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
                 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
                 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
-                case BluetoothDevice.UNBOND_REASON_REMOVED:
                     errorMsg = R.string.bluetooth_pairing_error_message;
                     break;
                 default:
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
index e7a20b3..c88ed8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
@@ -1,6 +1,7 @@
 # Default reviewers for this and subdirectories.
 andychou@google.com
 arcwang@google.com
+changbetty@google.com
 goldmanj@google.com
 qal@google.com
 wengsu@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index 0bde5c0..f3b600c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -2,6 +2,7 @@
 andychou@google.com
 arcwang@google.com
 asapperstein@google.com
+changbetty@google.com
 goldmanj@google.com
 qal@google.com
 wengsu@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 6a4d650..4bff78f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -353,21 +353,6 @@
     }
 
     @Test
-    public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() {
-        mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
-        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
-        mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
-        mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED);
-        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
-        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
-
-        mContext.sendBroadcast(mIntent);
-
-        verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
-                eq(R.string.bluetooth_pairing_error_message));
-    }
-
-    @Test
     public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
         mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index ed2b6c9..db9b83e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -153,6 +153,7 @@
         VALIDATORS.put(Secure.DOZE_TAP_SCREEN_GESTURE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DOZE_WAKE_DISPLAY_GESTURE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.DOZE_QUICK_PICKUP_GESTURE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NFC_PAYMENT_DEFAULT_COMPONENT, COMPONENT_NAME_VALIDATOR);
         VALIDATORS.put(
                 Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, NON_NEGATIVE_INTEGER_VALIDATOR);
@@ -280,5 +281,6 @@
         VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
                 new InclusiveFloatRangeValidator(0.0f, 1.0f));
         VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a0b9528..7288371 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1990,6 +1990,13 @@
         dumpSetting(s, p,
                 Settings.Secure.CARRIER_APPS_HANDLED,
                 SecureSettingsProto.CARRIER_APPS_HANDLED);
+
+        final long clipboardToken = p.start(SecureSettingsProto.CLIPBOARD);
+        dumpSetting(s, p,
+                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
+                SecureSettingsProto.Clipboard.SHOW_ACCESS_NOTIFICATIONS);
+        p.end(clipboardToken);
+
         dumpSetting(s, p,
                 Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
                 SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e427981..400742b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1937,8 +1937,11 @@
         if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
             return;
         }
-        checkReadableAnnotation(settingsType, settingName);
         ApplicationInfo ai = getCallingApplicationInfoOrThrow();
+        if (ai.isSystemApp() || ai.isSignedWithPlatformKey()) {
+            return;
+        }
+        checkReadableAnnotation(settingsType, settingName);
         if (!ai.isInstantApp()) {
             return;
         }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4dc6d14..c520568 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -745,6 +745,7 @@
                  Settings.Secure.SILENCE_GESTURE,
                  Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
                  Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
+                 Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL,
                  Settings.Secure.TAP_GESTURE,
                  Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a28a1e3..b4194fd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -426,6 +426,9 @@
     <!-- Permission required for CTS test - ClipboardManagerTest -->
     <uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
 
+    <!-- Permission required for CTS test - FontManagerTest -->
+    <uses-permission android:name="android.permission.UPDATE_FONTS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b6fd286..6574353 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -54,7 +54,7 @@
     name: "SystemUI-sensors",
     srcs: [
         "src/com/android/systemui/util/sensors/ThresholdSensor.java",
-    ]
+    ],
 }
 
 android_library {
@@ -99,7 +99,7 @@
         "SystemUI-tags",
         "SystemUI-proto",
         "dagger2",
-        "jsr330"
+        "jsr330",
     ],
     manifest: "AndroidManifest.xml",
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b6d942a..e0097df 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -578,7 +578,7 @@
 
         <!-- People Space UI Screen -->
         <activity android:name=".people.PeopleSpaceActivity"
-            android:label="People"
+            android:label="@string/people_tile_title"
             android:enabled="true"
             android:exported="true"
             android:theme="@android:style/Theme.Material.NoActionBar">
@@ -592,7 +592,7 @@
         <!-- People Space Widget -->
         <receiver
             android:name=".people.widget.PeopleSpaceWidgetProvider"
-            android:label="People Space"
+            android:label="@string/people_tile_title"
             android:enabled="true"
             android:exported="true">
             <intent-filter>
@@ -751,6 +751,17 @@
                   android:visibleToInstantApps="true">
         </activity>
 
+        <activity android:name=".controls.ui.ControlsActivity"
+                  android:label="@string/quick_controls_title"
+                  android:theme="@style/Theme.ControlsActivity"
+                  android:excludeFromRecents="true"
+                  android:showWhenLocked="true"
+                  android:showForAllUsers="true"
+                  android:launchMode="singleInstance"
+                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+                  android:visibleToInstantApps="true">
+        </activity>
+
         <receiver android:name=".controls.management.ControlsRequestReceiver"
             android:exported="true">
             <intent-filter>
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 9e67e4b..d6204db 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -25,7 +25,10 @@
 
     name: "SystemUIPluginLib",
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "bcsmartspace/src/**/*.java",
+    ],
 
     static_libs: [
         "PluginCoreLib",
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..f8a9a045
--- /dev/null
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+    int VERSION = 1;
+
+    /** Register a listener to get Smartspace data. */
+    void registerListener(SmartspaceTargetListener listener);
+
+    /** Unregister a listener. */
+    void unregisterListener(SmartspaceTargetListener listener);
+
+    /** Provides Smartspace data to registered listeners. */
+    interface SmartspaceTargetListener {
+        /** Each Parcelable is a SmartspaceTarget that represents a card. */
+        void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 4fc1973..0d348e2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -44,7 +44,7 @@
     /**
      * Returns true if the FalsingManager thinks the last gesure was not a valid tap.
      *
-     * Accepts one parameter, robustCheck, that distinctly changes behavior. When set to false,
+     * The first parameter, robustCheck, distinctly changes behavior. When set to false,
      * this method simply looks at the last gesture and returns whether it is a tap or not, (as
      * opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis
      * is performed that can include historical interactions and other contextual cues to see
@@ -53,8 +53,11 @@
      * Set robustCheck to true if you want to validate a tap for launching an action, like opening
      * a notification. Set to false if you simply want to know if the last gesture looked like a
      * tap.
+     *
+     * The second parameter, falsePenalty, indicates how much this should affect future gesture
+     * classifications if this tap looks like a false.
      */
-    boolean isFalseTap(boolean robustCheck);
+    boolean isFalseTap(boolean robustCheck, double falsePenalty);
 
     /**
      * Returns true if the last two gestures do not look like a double tap.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 53f7e44..ca13204 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,4 +50,8 @@
     public abstract void onStateChanged(State state);
 
     public abstract int getDetailY();
+
+    public View getLabelContainer() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
deleted file mode 100644
index 1ccb176..0000000
--- a/packages/SystemUI/res/drawable/controls_dialog_bg.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorBackground" />
-    <corners android:radius="@dimen/notification_corner_radius" />
-</shape>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml
similarity index 66%
copy from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
copy to packages/SystemUI/res/drawable/qs_tile_background.xml
index 644d1ada..265f575 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background.xml
@@ -14,9 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
-    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.UdfpsAnimationViewKeyguard>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask"
+        android:drawable="@drawable/qs_tile_background_shape" />
+    <item android:id="@id/background"
+        android:drawable="@drawable/qs_tile_background_shape"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
similarity index 68%
copy from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
copy to packages/SystemUI/res/drawable/qs_tile_background_shape.xml
index 644d1ada..f6b6834 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
@@ -14,9 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
-    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.UdfpsAnimationViewKeyguard>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="@dimen/qs_corner_radius" />
+    <solid android:color="#FFFFFF" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_detail_dialog.xml b/packages/SystemUI/res/layout/controls_detail_dialog.xml
index ee5315a..28fc863 100644
--- a/packages/SystemUI/res/layout/controls_detail_dialog.xml
+++ b/packages/SystemUI/res/layout/controls_detail_dialog.xml
@@ -20,8 +20,8 @@
     android:id="@+id/control_detail_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginTop="@dimen/controls_activity_view_top_offset"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:background="@android:color/black">
   <LinearLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -30,7 +30,7 @@
     <ImageView
         android:id="@+id/control_detail_close"
         android:contentDescription="@string/accessibility_desc_close"
-        android:src="@drawable/ic_close"
+        android:src="@drawable/ic_arrow_back"
         android:background="?android:attr/selectableItemBackgroundBorderless"
         android:tint="@color/control_primary_text"
         android:layout_width="48dp"
@@ -56,7 +56,6 @@
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1"
-      android:background="@android:color/black"
       android:orientation="vertical" />
 </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml
similarity index 86%
rename from packages/SystemUI/res/layout/controls_in_dialog.xml
rename to packages/SystemUI/res/layout/controls_fullscreen.xml
index 983999f..1b2d2e2 100644
--- a/packages/SystemUI/res/layout/controls_in_dialog.xml
+++ b/packages/SystemUI/res/layout/controls_fullscreen.xml
@@ -20,11 +20,8 @@
     android:id="@+id/control_detail_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
-    android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
-    android:padding="8dp"
     android:orientation="vertical"
-    android:background="@drawable/controls_dialog_bg">
+    android:background="@android:color/black">
 
   <com.android.systemui.globalactions.MinHeightScrollView
       android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index b060afd..9d01148 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -25,8 +25,21 @@
 
     <!-- make sure the header stays centered in the layout by adding a spacer -->
     <Space
+        android:id="@+id/controls_spacer"
         android:layout_width="@dimen/controls_header_menu_size"
-        android:layout_height="1dp" />
+        android:layout_height="1dp"
+        android:visibility="gone" />
+
+    <ImageView
+        android:id="@+id/controls_close"
+        android:contentDescription="@string/accessibility_desc_close"
+        android:src="@drawable/ic_close"
+        android:background="?android:attr/selectableItemBackgroundBorderless"
+        android:tint="@color/control_primary_text"
+        android:layout_width="@dimen/controls_header_menu_size"
+        android:layout_height="@dimen/controls_header_menu_size"
+        android:padding="12dp"
+        android:visibility="gone" />
     <!-- need to keep this outer view in order to have a correctly sized anchor
          for the dropdown menu, as well as dropdown background in the right place -->
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml
index 07af01b..1784cae 100644
--- a/packages/SystemUI/res/layout/people_space_activity.xml
+++ b/packages/SystemUI/res/layout/people_space_activity.xml
@@ -25,19 +25,30 @@
         android:layout_height="wrap_content"
         android:layout_marginTop="16dp"
         android:orientation="vertical"
-        android:padding="16dp"
+        android:padding="24dp"
         android:clipChildren="false"
         android:clipToPadding="false">
 
         <TextView
-            android:id="@+id/select_conversation"
-            android:text="@string/select_conversation_text"
-            android:layout_width="match_parent"
+            android:id="@+id/select_conversation_title"
+            android:gravity="center"
+            android:text="@string/select_conversation_title"
+            android:layout_width="wrap_content"
             android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
             android:textSize="24sp"
             android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"/>
+
+        <TextView
+            android:id="@+id/select_conversation"
+            android:gravity="center"
+            android:text="@string/select_conversation_text"
+            android:layout_width="match_parent"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+            android:textSize="16sp"
+            android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal"
-            android:paddingBottom="16dp" />
+            android:padding="24dp" />
 
     </LinearLayout>
 </androidx.core.widget.NestedScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
new file mode 100644
index 0000000..3ced1ff
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
@@ -0,0 +1,75 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <LinearLayout
+        android:background="@drawable/people_space_tile_view_card"
+        android:id="@+id/item"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:gravity="center"
+            android:paddingVertical="2dp"
+            android:paddingHorizontal="8dp"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <ImageView
+                android:background="@drawable/ic_person"
+                android:id="@+id/person_icon_only"
+                android:layout_width="60dp"
+                android:layout_height="60dp"/>
+
+            <LinearLayout
+                android:orientation="vertical"
+                android:paddingStart="8dp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+
+                <ImageView
+                    android:id="@+id/availability"
+                    android:layout_width="10dp"
+                    android:layout_height="10dp"
+                    android:background="@drawable/circle_green_10dp"/>
+                <TextView
+                    android:id="@+id/name"
+                    android:text="@string/empty_user_name"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textSize="14sp"
+                    android:maxLines="1"
+                    android:ellipsize="end"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"/>
+
+                <TextView
+                    android:id="@+id/last_interaction"
+                    android:text="@string/empty_status"
+                    android:textColor="?android:attr/textColorSecondary"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                    android:textSize="12sp"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:maxLines="3"
+                    android:ellipsize="end"/>
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index a5b8cfa..c521dc2 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -28,13 +28,6 @@
     android:clipToPadding="false"
     android:focusable="true" >
 
-    <include
-        layout="@layout/mobile_signal_group"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_carrier_margin_width"
-        android:visibility="gone" />
-
     <com.android.systemui.util.AutoMarqueeTextView
         android:id="@+id/qs_carrier_text"
         android:layout_width="wrap_content"
@@ -46,4 +39,11 @@
         android:singleLine="true"
         android:maxEms="7"/>
 
+    <include
+        layout="@layout/mobile_signal_group"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/qs_carrier_margin_width"
+        android:visibility="gone" />
+
 </com.android.systemui.qs.carrier.QSCarrier>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
deleted file mode 100644
index f32faa0..0000000
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.UdfpsAnimationViewFpmOther
-    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.UdfpsAnimationViewFpmOther>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_bp_view.xml
similarity index 88%
rename from packages/SystemUI/res/layout/udfps_animation_view_bp.xml
rename to packages/SystemUI/res/layout/udfps_bp_view.xml
index 0cfbf2e..f1c55ef 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
+++ b/packages/SystemUI/res/layout/udfps_bp_view.xml
@@ -14,9 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewBp
+<com.android.systemui.biometrics.UdfpsBpView
     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>
+</com.android.systemui.biometrics.UdfpsBpView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_enroll_view.xml
similarity index 79%
rename from packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
rename to packages/SystemUI/res/layout/udfps_enroll_view.xml
index 9b5752d..4035305 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
+++ b/packages/SystemUI/res/layout/udfps_enroll_view.xml
@@ -14,13 +14,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewEnroll
+<com.android.systemui.biometrics.UdfpsEnrollView
     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">
 
-    <!-- Enrollment progress bar-->
+    <!-- Enrollment progress bar -->
     <com.android.systemui.biometrics.UdfpsProgressBar
         android:id="@+id/progress_bar"
         android:layout_width="match_parent"
@@ -31,4 +31,9 @@
         android:layout_gravity="center"
         android:visibility="gone"/>
 
-</com.android.systemui.biometrics.UdfpsAnimationViewEnroll>
+    <!-- Fingerprint -->
+    <ImageView
+        android:id="@+id/udfps_enroll_animation_fp_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsEnrollView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
similarity index 74%
rename from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
rename to packages/SystemUI/res/layout/udfps_fpm_other_view.xml
index 644d1ada..6ecbb47 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
@@ -14,9 +14,15 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
+<com.android.systemui.biometrics.UdfpsFpmOtherView
     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.UdfpsAnimationViewKeyguard>
+
+    <!-- Fingerprint -->
+    <ImageView
+        android:id="@+id/udfps_fpm_other_fp_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsFpmOtherView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
similarity index 71%
copy from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
copy to packages/SystemUI/res/layout/udfps_keyguard_view.xml
index 644d1ada..0199ccb 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -14,9 +14,17 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
+<com.android.systemui.biometrics.UdfpsKeyguardView
     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.UdfpsAnimationViewKeyguard>
+
+    <!-- TODO: add background protection -->
+
+    <!-- Fingerprint -->
+    <ImageView
+        android:id="@+id/udfps_keyguard_animation_fp_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsKeyguardView>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index e24c9e9..50b2f20 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -22,6 +22,11 @@
     android:layout_height="match_parent"
     systemui:sensorTouchAreaCoefficient="0.5">
 
+    <ViewStub
+        android:id="@+id/animation_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
     <com.android.systemui.biometrics.UdfpsSurfaceView
         android:id="@+id/hbm_view"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index da58a34..b0c5239 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -180,6 +180,9 @@
     <!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
     <integer name="doze_pickup_vibration_threshold">2000</integer>
 
+    <!-- Doze: quick pickup duration to stay in AOD until the next gesture is triggered -->
+    <integer name="doze_quick_pickup_aod_duration">5000</integer>
+
     <!-- Type of a sensor that provides a low-power estimate of the desired display
          brightness, suitable to listen to while the device is asleep (e.g. during
          always-on display) -->
@@ -577,9 +580,15 @@
     <!-- Whether wallet view is shown in landscape / seascape orientations -->
     <bool name="global_actions_show_landscape_wallet_view">false</bool>
 
+    <!-- Package name of the preferred system app to perform eSOS action -->
+    <string name="config_preferredEmergencySosPackage" translatable="false"></string>
+
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">false</bool>
 
     <!-- Determines whether the shell features all run on another thread. -->
     <bool name="config_enableShellMainThread">false</bool>
+
+    <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
+    <bool name="allow_force_nav_bar_handle_opaque">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c0e474a..2062104 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -171,7 +171,7 @@
     <dimen name="scroll_fast_threshold">1500dp</dimen>
 
     <!-- Height of a the shelf with the notification icons -->
-    <dimen name="notification_shelf_height">32dp</dimen>
+    <dimen name="notification_shelf_height">48dp</dimen>
 
     <!-- Minimum height of a notification to be interactable -->
     <dimen name="notification_min_interaction_height">40dp</dimen>
@@ -512,6 +512,7 @@
     <!-- The size of the gesture span needed to activate the "pull" notification expansion -->
     <dimen name="pull_span_min">25dp</dimen>
 
+    <dimen name="qs_corner_radius">14dp</dimen>
     <dimen name="qs_tile_height">96dp</dimen>
     <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
     <dimen name="qs_tile_layout_margin_side">18dp</dimen>
@@ -528,6 +529,8 @@
     <dimen name="qs_tile_margin_top">0dp</dimen>
     <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
     <dimen name="qs_tile_background_size">44dp</dimen>
+    <dimen name="qs_icon_size">20dp</dimen>
+    <dimen name="qs_label_container_margin">10dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fcda80d..db79e96 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -851,8 +851,6 @@
     <string name="quick_settings_wifi_label">Wi-Fi</string>
     <!-- QuickSettings: Internet [CHAR LIMIT=NONE] -->
     <string name="quick_settings_internet_label">Internet</string>
-    <!-- QuickSettings: Airplane-safe [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_airplane_safe_label">Airplane-safe</string>
     <!-- QuickSettings: networks available [CHAR LIMIT=NONE] -->
     <string name="quick_settings_networks_available">Networks available</string>
     <!-- QuickSettings: networks unavailable [CHAR LIMIT=NONE] -->
@@ -2297,6 +2295,9 @@
     <!-- accessibility label for button to select user [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_user">Signed in as <xliff:g name="user" example="John">%s</xliff:g></string>
 
+    <!-- Accessibility description (not shown on the screen) of action to open user switcher when the multi-user icon is clicked. It will read as "Double-tap to choose user" in screen readers [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_choose_user_action">choose user</string>
+
     <!-- Content description for no internet connection [CHAR LIMIT=NONE] -->
     <string name="data_connection_no_internet">No internet</string>
 
@@ -2651,7 +2652,7 @@
     <!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
     <string name="magnification_mode_switch_description">Magnification switch</string>
     <!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
-    <string name="magnification_mode_switch_state_full_screen">Magnify entire screen</string>
+    <string name="magnification_mode_switch_state_full_screen">Magnify full screen</string>
     <!-- A11y state description for magnification mode switch that device is in window mode. [CHAR LIMIT=NONE] -->
     <string name="magnification_mode_switch_state_window">Magnify part of screen</string>
     <!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
@@ -2811,44 +2812,44 @@
     <!-- Text to display when copying the build number off QS [CHAR LIMIT=NONE]-->
     <string name="build_number_copy_toast">Build number copied to clipboard.</string>
 
-    <!-- Status for last interaction with exact time [CHAR LIMIT=120] -->
-    <string name="last_interaction_status" translatable="false">Last chatted <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
-    <!-- Status for last interaction when less than a certain time window [CHAR LIMIT=120] -->
-    <string name="last_interaction_status_less_than" translatable="false">Last chatted less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
-    <!-- Status for last interaction when over a certain time window [CHAR LIMIT=120] -->
-    <string name="last_interaction_status_over" translatable="false">Last chatted over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
-    <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
-    <string name="basic_status" translatable="false">Open conversation</string>
-    <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
-    <string name="select_conversation_text" translatable="false">Select one conversation to show in your widget:</string>
-    <!-- Timestamp for notification with exact time [CHAR LIMIT=120] -->
-    <string name="timestamp" translatable="false"><xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
-    <!-- Timestamp for notification when less than a certain time window [CHAR LIMIT=120] -->
-    <string name="less_than_timestamp" translatable="false">Less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
-    <!-- Timestamp for notification when over a certain time window [CHAR LIMIT=120] -->
-    <string name="over_timestamp" translatable="false">Over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
-    <!-- Status text for a birthday today [CHAR LIMIT=30] -->
-    <string name="birthday_status" translatable="false">Birthday</string>
-    <!-- Status text for an upcoming birthday [CHAR LIMIT=30] -->
-    <string name="upcoming_birthday_status" translatable="false">Birthday soon</string>
-    <!-- Status text for an anniversary [CHAR LIMIT=30] -->
-    <string name="anniversary_status" translatable="false">Anniversary</string>
-    <!-- Status text for sharing location [CHAR LIMIT=30] -->
-    <string name="location_status" translatable="false">Sharing location</string>
-    <!-- Status text for a new story posted [CHAR LIMIT=30] -->
-    <string name="new_story_status" translatable="false">New story</string>
-    <!-- Status text for watching a video [CHAR LIMIT=30] -->
-    <string name="video_status" translatable="false">Watching</string>
-    <!-- Status text for listening to audio [CHAR LIMIT=30] -->
-    <string name="audio_status" translatable="false">Listening</string>
-    <!-- Status text for playing a game [CHAR LIMIT=30] -->
-    <string name="game_status" translatable="false">Playing</string>
-    <!-- Empty user name before user has selected a friend [CHAR LIMIT=30] -->
-    <string name="empty_user_name" translatable="false">Your friend</string>
-    <!-- Empty status shown before user has selected a friend [CHAR LIMIT=30] -->
-    <string name="empty_status" translatable="false">Their status</string>
-    <!-- Default text for missed call notifications [CHAR LIMIT=30] -->
-    <string name="missed_call" translatable="false">Missed call</string>
+     <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
+    <string name="basic_status">Open conversation</string>
+    <!--Title text for Conversation widget set up screen [CHAR LIMIT=180] -->
+    <string name="select_conversation_title">Conversation widgets</string>
+    <!--Text explaining to tap a conversation to select it show in their Conversation widget [CHAR LIMIT=180] -->
+    <string name="select_conversation_text">Tap a conversation to add it to your Home screen</string>
+    <!-- Timestamp for notification with exact time [CHAR LIMIT=25] -->
+    <string name="timestamp"><xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
+    <!-- Timestamp for notification when less than a certain time window [CHAR LIMIT=25] -->
+    <string name="less_than_timestamp">Less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
+    <!-- Timestamp for notification when over a certain time window [CHAR LIMIT=25] -->
+    <string name="over_timestamp">Over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
+    <!-- Status text on the Conversation widget for a birthday today [CHAR LIMIT=20] -->
+    <string name="birthday_status">Birthday</string>
+    <!-- Status text on the Conversation widget for an upcoming birthday [CHAR LIMIT=20] -->
+    <string name="upcoming_birthday_status">Birthday soon</string>
+    <!-- Status text on the Conversation widget for an anniversary [CHAR LIMIT=20] -->
+    <string name="anniversary_status">Anniversary</string>
+    <!-- Status text on the Conversation widget for sharing location [CHAR LIMIT=20] -->
+    <string name="location_status">Sharing location</string>
+    <!-- Status text on the Conversation widget for a new story posted [CHAR LIMIT=20] -->
+    <string name="new_story_status">New story</string>
+    <!-- Status text on the Conversation widget for watching a video [CHAR LIMIT=20] -->
+    <string name="video_status">Watching</string>
+    <!-- Status text on the Conversation widget for listening to audio [CHAR LIMIT=20] -->
+    <string name="audio_status">Listening</string>
+    <!-- Status text on the Conversation widget for playing a game [CHAR LIMIT=20] -->
+    <string name="game_status">Playing</string>
+    <!-- Empty user name before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
+    <string name="empty_user_name">Friends</string>
+    <!-- Empty status shown before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
+    <string name="empty_status">Let’s chat tonight!</string>
+    <!-- Default text for missed call notifications on their Conversation widget [CHAR LIMIT=20] -->
+    <string name="missed_call">Missed call</string>
+    <!-- Description text for adding a Conversation widget [CHAR LIMIT=100] -->
+    <string name="people_tile_description">See recent messages, missed calls, and status updates</string>
+    <!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
+    <string name="people_tile_title">Conversation</string>
 
     <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
     [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3c48548..fb885cb 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -649,6 +649,16 @@
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
     </style>
 
+    <style name="Theme.ControlsActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+        <item name="android:windowActivityTransitions">true</item>
+        <item name="android:windowContentTransitions">false</item>
+        <item name="android:windowIsTranslucent">false</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:statusBarColor">@*android:color/transparent</item>
+        <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
+    </style>
+
     <style name="Theme.CreateUser" parent="@style/Theme.SystemUI">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">#33000000</item>
@@ -660,30 +670,12 @@
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
-    <style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
-      <item name="android:windowAnimationStyle">@style/Animation.Bottomsheet</item>
-      <item name="android:windowFullscreen">true</item>
+     <style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+      <item name="android:windowFullscreen">false</item>
       <item name="android:windowIsFloating">false</item>
-      <item name="android:windowBackground">@null</item>
-      <item name="android:backgroundDimEnabled">true</item>
-    </style>
-
-    <style name="Animation.Bottomsheet">
-      <item name="android:windowEnterAnimation">@anim/bottomsheet_in</item>
-      <item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
-    </style>
-
-    <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
-      <item name="android:windowAnimationStyle">@style/Animation.ControlDialog</item>
-      <item name="android:windowFullscreen">true</item>
-      <item name="android:windowIsFloating">false</item>
-      <item name="android:windowBackground">@null</item>
-      <item name="android:backgroundDimEnabled">true</item>
-    </style>
-
-    <style name="Animation.ControlDialog">
-      <item name="android:windowEnterAnimation">@*android:anim/dialog_enter</item>
-      <item name="android:windowExitAnimation">@*android:anim/dialog_exit</item>
+      <item name="android:windowBackground">@android:color/black</item>
+      <item name="android:backgroundDimEnabled">false</item>
+      <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
     </style>
 
     <style name="Control" />
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index fbdac5e1..35188d8 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -20,8 +20,9 @@
     android:minResizeWidth="110dp"
     android:minResizeHeight="55dp"
     android:updatePeriodMillis="60000"
-    android:previewImage="@drawable/ic_person"
+    android:description="@string/people_tile_description"
+    android:previewLayout="@layout/people_space_placeholder_layout"
     android:resizeMode="horizontal|vertical"
     android:configure="com.android.systemui.people.PeopleSpaceActivity"
-    android:initialLayout="@layout/people_space_widget">
+    android:initialLayout="@layout/people_space_placeholder_layout">
 </appwidget-provider>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 72e4061..f50c3c92 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -20,7 +20,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.view.Choreographer;
-import android.view.Surface;
 import android.view.SurfaceControl;
 
 /**
@@ -91,24 +90,6 @@
                 .setPosition(leash, positionX, positionY);
     }
 
-    public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds,
-            @Surface.Rotation int rotation) {
-        resetScale(tx, leash, destinationBounds);
-        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
-            final int degree = (rotation == Surface.ROTATION_90) ? -90 : 90;
-            mTmpTransform.setRotate(degree, 0, 0);
-            tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
-        }
-        resetCornerRadius(tx, leash);
-        crop(tx, leash, destinationBounds);
-    }
-
-    public void resetScale(SurfaceControl.Transaction tx, SurfaceControl leash,
-            Rect destinationBounds) {
-        tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9)
-                .setPosition(leash, destinationBounds.left, destinationBounds.top);
-    }
-
     public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) {
         tx.setCornerRadius(leash, 0);
     }
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 49e86f5..5126284 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
@@ -63,15 +63,9 @@
     Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
 
     /**
-     * Control the {@param alpha} of the back button in the navigation bar and {@param animate} if
-     * needed from current value
-     * @deprecated
-     */
-    void setBackButtonAlpha(float alpha, boolean animate) = 8;
-
-    /**
      * Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
-     * and home bar in no-button mode) and {@param animate} if needed from current value
+     * and home handle & background in gestural mode).  The {@param animate} is currently only
+     * supported for 2 button mode.
      */
     void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index ebb6e30..e9e9b24 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -289,15 +289,19 @@
     /**
      * Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
      */
-    public float getVisibleThumbnailRatio() {
+    public float getVisibleThumbnailRatio(boolean clipInsets) {
         if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
             return 0f;
         }
 
-        float availableWidth = lastSnapshotData.taskSize.x - (lastSnapshotData.contentInsets.left
-                + lastSnapshotData.contentInsets.right);
-        float availableHeight = lastSnapshotData.taskSize.y - (lastSnapshotData.contentInsets.top
-                + lastSnapshotData.contentInsets.bottom);
+        float availableWidth = lastSnapshotData.taskSize.x;
+        float availableHeight = lastSnapshotData.taskSize.y;
+        if (clipInsets) {
+            availableWidth -=
+                    (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right);
+            availableHeight -=
+                    (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom);
+        }
         return availableWidth / availableHeight;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index af7c5da..bf0d29a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.shared.system;
 
-import android.window.TaskSnapshot;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IRecentsAnimationController;
+import android.window.TaskSnapshot;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -76,10 +76,14 @@
      * accordingly. This should be called before `finish`
      * @param taskId Task id of the Activity in PiP mode.
      * @param destinationBounds Bounds of the PiP window on home.
+     * @param windowCrop bounds to crop as part of final transform.
+     * @param float9 An array of 9 floats to be used as matrix transform.
      */
-    public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+    public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
+            float[] float9) {
         try {
-            mAnimationController.setFinishTaskBounds(taskId, destinationBounds);
+            mAnimationController.setFinishTaskBounds(taskId, destinationBounds, windowCrop,
+                    float9);
         } catch (RemoteException e) {
             Log.d(TAG, "Failed to set finish task bounds", e);
         }
@@ -141,9 +145,9 @@
     /**
      * @see IRecentsAnimationController#detachNavigationBarFromApp
      */
-    public void detachNavigationBarFromApp() {
+    public void detachNavigationBarFromApp(boolean moveHomeToTop) {
         try {
-            mAnimationController.detachNavigationBarFromApp();
+            mAnimationController.detachNavigationBarFromApp(moveHomeToTop);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to detach the navigation bar from app", e);
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 7ba9069..06155bc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -137,8 +137,11 @@
             mWrapped.hideCurrentInputMethod();
         }
 
-        @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
-            if (mWrapped != null) mWrapped.setFinishTaskBounds(taskId, destinationBounds);
+        @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds,
+                Rect windowCrop, float[] float9) {
+            if (mWrapped != null) {
+                mWrapped.setFinishTaskBounds(taskId, destinationBounds, windowCrop, float9);
+            }
         }
 
         @Override public void finish(boolean toHome, boolean sendUserLeaveHint) {
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 1569fff..3f0e3eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -45,6 +45,7 @@
     private int mLockScreenColor;
 
     private boolean mIsDozing;
+    private float mDozeAmount;
     private Locale mLocale;
 
     private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
@@ -59,6 +60,7 @@
         super(view);
         mStatusBarStateController = statusBarStateController;
         mIsDozing = mStatusBarStateController.isDozing();
+        mDozeAmount = mStatusBarStateController.getDozeAmount();
         mBroadcastDispatcher = broadcastDispatcher;
 
         mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
@@ -82,6 +84,7 @@
                 new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mIsDozing = mStatusBarStateController.isDozing();
+        mDozeAmount = mStatusBarStateController.getDozeAmount();
         refreshTime();
         initColors();
     }
@@ -136,9 +139,15 @@
     private final StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
-                public void onDozingChanged(boolean isDozing) {
-                    mIsDozing = isDozing;
-                    mView.animateDoze(mIsDozing, true);
+                public void onDozeAmountChanged(float linear, float eased) {
+                    boolean noAnimation = (mDozeAmount == 0f && linear == 1f)
+                            || (mDozeAmount == 1f && linear == 0f);
+                    boolean isDozing = linear > mDozeAmount;
+                    mDozeAmount = linear;
+                    if (mIsDozing != isDozing) {
+                        mIsDozing = isDozing;
+                        mView.animateDoze(mIsDozing, !noAnimation);
+                    }
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 5760565..9f32c03 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -35,12 +35,15 @@
 import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingClassifier;
+import com.android.systemui.classifier.FalsingCollector;
 
 public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
         extends KeyguardInputViewController<T> {
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockPatternUtils mLockPatternUtils;
     private final LatencyTracker mLatencyTracker;
+    private final FalsingCollector mFalsingCollector;
     private CountDownTimer mCountdownTimer;
     protected KeyguardMessageAreaController mMessageAreaController;
     private boolean mDismissing;
@@ -70,11 +73,12 @@
             LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker) {
+            LatencyTracker latencyTracker, FalsingCollector falsingCollector) {
         super(view, securityMode, keyguardSecurityCallback);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mLatencyTracker = latencyTracker;
+        mFalsingCollector = falsingCollector;
         KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
         mMessageAreaController = messageAreaControllerFactory.create(kma);
     }
@@ -181,8 +185,8 @@
             if (timeoutMs == 0) {
                 mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
             }
+            mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
         }
-        mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
     }
 
     protected void verifyPasswordAndUnlock() {
@@ -256,6 +260,7 @@
     }
 
     protected void onUserInput() {
+        mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.passed(0.6));
         getKeyguardSecurityCallback().userActivity();
         getKeyguardSecurityCallback().onUserInput();
         mMessageAreaController.setMessage("");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 5e02e04..40190c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -26,16 +26,12 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.jank.InteractionJankMonitor;
-import com.android.systemui.Gefingerpoken;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A Base class for all Keyguard password/pattern/pin related inputs.
  */
 public abstract class KeyguardInputView extends LinearLayout {
-    private final List<Gefingerpoken> mMotionEventListener = new ArrayList<>();
+    private Runnable mOnFinishImeAnimationRunnable;
 
     public KeyguardInputView(Context context) {
         super(context);
@@ -52,7 +48,7 @@
 
     abstract CharSequence getTitle();
 
-    void animateForIme(float interpolatedFraction) {
+    void animateForIme(float interpolatedFraction, boolean appearingAnim) {
         return;
     }
 
@@ -66,27 +62,6 @@
         return false;
     }
 
-    void addMotionEventListener(Gefingerpoken listener) {
-        mMotionEventListener.add(listener);
-    }
-
-    void removeMotionEventListener(Gefingerpoken listener) {
-        mMotionEventListener.remove(listener);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        return mMotionEventListener.stream().anyMatch(listener -> listener.onTouchEvent(event))
-                || super.onTouchEvent(event);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        return mMotionEventListener.stream().anyMatch(
-                listener -> listener.onInterceptTouchEvent(event))
-                || super.onInterceptTouchEvent(event);
-    }
-
     protected AnimatorListenerAdapter getAnimationListener(int cuj) {
         return new AnimatorListenerAdapter() {
             private boolean mIsCancel;
@@ -112,4 +87,14 @@
         };
     }
 
+    public void setOnFinishImeAnimationRunnable(Runnable onFinishImeAnimationRunnable) {
+        mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
+    }
+
+    public void runOnFinishImeAnimationRunnable() {
+        if (mOnFinishImeAnimationRunnable != null) {
+            mOnFinishImeAnimationRunnable.run();
+            mOnFinishImeAnimationRunnable = null;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 957882d..05f33a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -155,8 +155,8 @@
         private final InputMethodManager mInputMethodManager;
         private final DelayableExecutor mMainExecutor;
         private final Resources mResources;
-        private LiftToActivateListener mLiftToActivateListener;
-        private TelephonyManager mTelephonyManager;
+        private final LiftToActivateListener mLiftToActivateListener;
+        private final TelephonyManager mTelephonyManager;
         private final FalsingCollector mFalsingCollector;
         private final boolean mIsNewLayoutEnabled;
 
@@ -167,8 +167,7 @@
                 KeyguardMessageAreaController.Factory messageAreaControllerFactory,
                 InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
                 @Main Resources resources, LiftToActivateListener liftToActivateListener,
-                TelephonyManager telephonyManager,
-                FalsingCollector falsingCollector,
+                TelephonyManager telephonyManager, FalsingCollector falsingCollector,
                 FeatureFlags featureFlags) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
@@ -189,12 +188,13 @@
             if (keyguardInputView instanceof KeyguardPatternView) {
                 return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
-                        keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
+                        keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
+                        mMessageAreaControllerFactory);
             } else if (keyguardInputView instanceof KeyguardPasswordView) {
                 return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mInputMethodManager, mMainExecutor, mResources);
+                        mInputMethodManager, mMainExecutor, mResources, mFalsingCollector);
             } else if (keyguardInputView instanceof KeyguardPINView) {
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -204,14 +204,14 @@
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager,
-                        mFalsingCollector, mIsNewLayoutEnabled);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
+                        mIsNewLayoutEnabled);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager,
-                        mFalsingCollector, mIsNewLayoutEnabled);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
+                        mIsNewLayoutEnabled);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 533bec1..cd9627f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static android.view.WindowInsets.Type.ime;
+
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
@@ -23,15 +25,25 @@
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.TextViewInputDisabler;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 /**
  * Displays an alphanumeric (latin-1) key entry for the user to enter
@@ -41,6 +53,8 @@
 
     private final int mDisappearYTranslation;
 
+    private static final long IME_DISAPPEAR_DURATION_MS = 125;
+
     // A delay constant to be used in a workaround for the situation where InputMethodManagerService
     // is not switched to the new user yet.
     // TODO: Remove this by ensuring such a race condition never happens.
@@ -150,9 +164,63 @@
     }
 
     @Override
-    public void animateForIme(float interpolatedFraction) {
+    public boolean startDisappearAnimation(Runnable finishRunnable) {
+        getWindowInsetsController().controlWindowInsetsAnimation(ime(),
+                100,
+                Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
+
+                    @Override
+                    public void onReady(@NonNull WindowInsetsAnimationController controller,
+                            int types) {
+                        ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+                        anim.addUpdateListener(animation -> {
+                            if (controller.isCancelled()) {
+                                return;
+                            }
+                            Insets shownInsets = controller.getShownStateInsets();
+                            Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
+                                    (int) (-shownInsets.bottom / 4
+                                            * anim.getAnimatedFraction())));
+                            controller.setInsetsAndAlpha(insets,
+                                    (float) animation.getAnimatedValue(),
+                                    anim.getAnimatedFraction());
+                        });
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationStart(Animator animation) {
+                            }
+
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                controller.finish(false);
+                                runOnFinishImeAnimationRunnable();
+                                finishRunnable.run();
+                            }
+                        });
+                        anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+                        anim.start();
+                    }
+
+                    @Override
+                    public void onFinished(
+                            @NonNull WindowInsetsAnimationController controller) {
+                    }
+
+                    @Override
+                    public void onCancelled(
+                            @Nullable WindowInsetsAnimationController controller) {
+                    }
+                });
+        return true;
+    }
+
+
+    @Override
+    public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
         animate().cancel();
-        setAlpha(Math.max(interpolatedFraction, getAlpha()));
+        setAlpha(appearingAnim
+                ? Math.max(interpolatedFraction, getAlpha())
+                : 1 - interpolatedFraction);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0f1c3c8..e45dd8b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -40,6 +40,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -112,9 +113,10 @@
             LatencyTracker latencyTracker,
             InputMethodManager inputMethodManager,
             @Main DelayableExecutor mainExecutor,
-            @Main Resources resources) {
+            @Main Resources resources,
+            FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker);
+                messageAreaControllerFactory, latencyTracker, falsingCollector);
         mKeyguardSecurityCallback = keyguardSecurityCallback;
         mInputMethodManager = inputMethodManager;
         mMainExecutor = mainExecutor;
@@ -187,23 +189,22 @@
             return;
         }
         if (wasDisabled) {
-            mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+            showInput();
         }
     }
 
     @Override
     public void onResume(int reason) {
         super.onResume(reason);
-
-        mPasswordEntry.requestFocus();
         if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
             showInput();
         }
     }
 
     private void showInput() {
-        mPasswordEntry.post(() -> {
-            if (mPasswordEntry.isFocused() && mView.isShown()) {
+        mView.post(() -> {
+            if (mView.isShown()) {
+                mPasswordEntry.requestFocus();
                 mInputMethodManager.showSoftInput(
                         mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
             }
@@ -212,7 +213,18 @@
 
     @Override
     public void onPause() {
-        super.onPause();
+        if (!mPasswordEntry.isVisibleToUser()) {
+            // Reset all states directly and then hide IME when the screen turned off.
+            super.onPause();
+        } else {
+            // In order not to break the IME hide animation by resetting states too early after
+            // the password checked, make sure resetting states after the IME hiding animation
+            // finished.
+            mView.setOnFinishImeAnimationRunnable(() -> {
+                mPasswordEntry.clearFocus();
+                super.onPause();
+            });
+        }
         mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 2aaf748..4f48bb4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -23,6 +23,7 @@
 import android.os.AsyncTask;
 import android.os.CountDownTimer;
 import android.os.SystemClock;
+import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.internal.util.LatencyTracker;
@@ -35,6 +36,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingClassifier;
+import com.android.systemui.classifier.FalsingCollector;
 
 import java.util.List;
 
@@ -50,6 +53,7 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockPatternUtils mLockPatternUtils;
     private final LatencyTracker mLatencyTracker;
+    private final FalsingCollector mFalsingCollector;
     private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
 
     private KeyguardMessageAreaController mMessageAreaController;
@@ -102,6 +106,11 @@
 
             final int userId = KeyguardUpdateMonitor.getCurrentUser();
             if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+                // Treat single-sized patterns as erroneous taps.
+                if (pattern.size() == 1) {
+                    mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed(
+                            0.7, "empty pattern input"));
+                }
                 mLockPatternView.enableInput();
                 onPatternChecked(userId, false, 0, false /* not valid - too short */);
                 return;
@@ -179,11 +188,13 @@
             LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             LatencyTracker latencyTracker,
+            FalsingCollector falsingCollector,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
         super(view, securityMode, keyguardSecurityCallback);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mLatencyTracker = latencyTracker;
+        mFalsingCollector = falsingCollector;
         mMessageAreaControllerFactory = messageAreaControllerFactory;
         KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
         mMessageAreaController = mMessageAreaControllerFactory.create(kma);
@@ -205,6 +216,12 @@
                 KeyguardUpdateMonitor.getCurrentUser()));
         // vibrate mode will be the same for the life of this screen
         mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+        mLockPatternView.setOnTouchListener((v, event) -> {
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mFalsingCollector.avoidGesture();
+            }
+            return false;
+        });
 
         EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
         if (button != null) {
@@ -224,6 +241,7 @@
     protected void onViewDetached() {
         super.onViewDetached();
         mLockPatternView.setOnPatternListener(null);
+        mLockPatternView.setOnTouchListener(null);
         EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
         if (button != null) {
             button.setCallback(null);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 4e06491..09fb8ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -164,6 +164,10 @@
         reloadColors();
     }
 
+    NumPadKey[] getButtons() {
+        return mButtons;
+    }
+
     /**
      * By default, the new layout will be enabled. When false, revert to the old style.
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index f247948..b156f81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,7 +25,6 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 
@@ -50,19 +49,6 @@
         return false;
     };
 
-    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
-        @Override
-        public boolean onInterceptTouchEvent(MotionEvent ev) {
-            mFalsingCollector.avoidGesture();
-            return false;
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent ev) {
-            return false;
-        }
-    };
-
     protected KeyguardPinBasedInputViewController(T view,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecurityMode securityMode,
@@ -73,7 +59,7 @@
             LiftToActivateListener liftToActivateListener,
             FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker);
+                messageAreaControllerFactory, latencyTracker, falsingCollector);
         mLiftToActivateListener = liftToActivateListener;
         mFalsingCollector = falsingCollector;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
@@ -83,8 +69,14 @@
     protected void onViewAttached() {
         super.onViewAttached();
 
-        mView.addMotionEventListener(mGlobalTouchListener);
-
+        for (NumPadKey button: mView.getButtons()) {
+            button.setOnTouchListener((v, event) -> {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                    mFalsingCollector.avoidGesture();
+                }
+                return false;
+            });
+        }
         mPasswordEntry.setOnKeyListener(mOnKeyListener);
         mPasswordEntry.setUserActivityListener(this::onUserInput);
 
@@ -123,7 +115,10 @@
     @Override
     protected void onViewDetached() {
         super.onViewDetached();
-        mView.removeMotionEventListener(mGlobalTouchListener);
+
+        for (NumPadKey button: mView.getButtons()) {
+            button.setOnTouchListener(null);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a2d7707..4887767 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -23,11 +23,9 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
-import android.graphics.Insets;
 import android.graphics.Rect;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -42,12 +40,9 @@
 import android.view.ViewPropertyAnimator;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
-import android.view.WindowInsetsAnimationControlListener;
-import android.view.WindowInsetsAnimationController;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -58,10 +53,12 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class KeyguardSecurityContainer extends FrameLayout {
@@ -97,6 +94,7 @@
     private final ViewConfiguration mViewConfiguration;
     private final SpringAnimation mSpringAnimation;
     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
+    private final List<Gefingerpoken> mMotionEventListeners = new ArrayList<>();
 
     private float mLastTouchY = -1;
     private int mActivePointerId = -1;
@@ -127,6 +125,9 @@
                         WindowInsetsAnimation.Bounds bounds) {
                     if (!mDisappearAnimRunning) {
                         beginJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
+                    } else {
+                        beginJankInstrument(
+                                InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
                     }
                     mSecurityViewFlipper.getBoundsOnScreen(mFinalBounds);
                     return bounds;
@@ -135,25 +136,28 @@
                 @Override
                 public WindowInsets onProgress(WindowInsets windowInsets,
                         List<WindowInsetsAnimation> list) {
-                    if (mDisappearAnimRunning) {
-                        mSecurityViewFlipper.setTranslationY(
-                                mInitialBounds.bottom - mFinalBounds.bottom);
-                    } else {
-                        int translationY = 0;
-                        float interpolatedFraction = 1f;
-                        for (WindowInsetsAnimation animation : list) {
-                            if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
-                                continue;
-                            }
-                            interpolatedFraction = animation.getInterpolatedFraction();
-
-                            final int paddingBottom = (int) MathUtils.lerp(
-                                    mInitialBounds.bottom - mFinalBounds.bottom, 0,
-                                    interpolatedFraction);
-                            translationY += paddingBottom;
+                    float start = mDisappearAnimRunning
+                            ? -(mFinalBounds.bottom - mInitialBounds.bottom)
+                            : mInitialBounds.bottom - mFinalBounds.bottom;
+                    float end = mDisappearAnimRunning
+                            ? -((mFinalBounds.bottom - mInitialBounds.bottom) * 0.75f)
+                            : 0f;
+                    int translationY = 0;
+                    float interpolatedFraction = 1f;
+                    for (WindowInsetsAnimation animation : list) {
+                        if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
+                            continue;
                         }
-                        mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction);
+                        interpolatedFraction = animation.getInterpolatedFraction();
+
+                        final int paddingBottom = (int) MathUtils.lerp(
+                                start, end,
+                                interpolatedFraction);
+                        translationY += paddingBottom;
                     }
+                    mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
+                            !mDisappearAnimRunning);
+
                     return windowInsets;
                 }
 
@@ -161,7 +165,10 @@
                 public void onEnd(WindowInsetsAnimation animation) {
                     if (!mDisappearAnimRunning) {
                         endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
-                        mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f);
+                        mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
+                                true /* appearingAnim */);
+                    } else {
+                        endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
                     }
                 }
             };
@@ -388,6 +395,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        boolean result =  mMotionEventListeners.stream().anyMatch(
+                listener -> listener.onInterceptTouchEvent(event))
+                || super.onInterceptTouchEvent(event);
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 int pointerIndex = event.getActionIndex();
@@ -418,12 +429,17 @@
                 mIsDragging = false;
                 break;
         }
-        return false;
+        return result;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
+
+        boolean result =  mMotionEventListeners.stream()
+                .anyMatch(listener -> listener.onTouchEvent(event))
+                || super.onTouchEvent(event);
+
         switch (action) {
             case MotionEvent.ACTION_MOVE:
                 mVelocityTracker.addMovement(event);
@@ -469,6 +485,14 @@
         return true;
     }
 
+    void addMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListeners.add(listener);
+    }
+
+    void removeMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListeners.remove(listener);
+    }
+
     private void handleTap(MotionEvent event) {
         // If we're using a fullscreen security mode, skip
         if (!mOneHandedMode) {
@@ -502,63 +526,6 @@
 
     public void startDisappearAnimation(SecurityMode securitySelection) {
         mDisappearAnimRunning = true;
-        if (securitySelection == SecurityMode.Password) {
-            mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
-                    IME_DISAPPEAR_DURATION_MS,
-                    Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
-
-
-                        @Override
-                        public void onReady(@NonNull WindowInsetsAnimationController controller,
-                                int types) {
-                            ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
-                            anim.addUpdateListener(animation -> {
-                                if (controller.isCancelled()) {
-                                    return;
-                                }
-                                Insets shownInsets = controller.getShownStateInsets();
-                                Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
-                                        (int) (-shownInsets.bottom / 4
-                                                * anim.getAnimatedFraction())));
-                                controller.setInsetsAndAlpha(insets,
-                                        (float) animation.getAnimatedValue(),
-                                        anim.getAnimatedFraction());
-                            });
-                            anim.addListener(new AnimatorListenerAdapter() {
-                                @Override
-                                public void onAnimationStart(Animator animation) {
-                                    beginJankInstrument(
-                                            InteractionJankMonitor
-                                                    .CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
-                                }
-
-                                @Override
-                                public void onAnimationEnd(Animator animation) {
-                                    endJankInstrument(
-                                            InteractionJankMonitor
-                                                    .CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
-                                    controller.finish(false);
-                                }
-                            });
-                            anim.setDuration(IME_DISAPPEAR_DURATION_MS);
-                            anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-                            anim.start();
-                        }
-
-                        @Override
-                        public void onFinished(
-                                @NonNull WindowInsetsAnimationController controller) {
-                            mDisappearAnimRunning = false;
-                        }
-
-                        @Override
-                        public void onCancelled(
-                                @Nullable WindowInsetsAnimationController controller) {
-                            cancelJankInstrument(
-                                    InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
-                        }
-                    });
-        }
     }
 
     private void beginJankInstrument(int cuj) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db..7eac903 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -33,6 +33,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
+import android.view.MotionEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -46,6 +47,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
 import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Gefingerpoken;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,9 +74,44 @@
     private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
     private final SecurityCallback mSecurityCallback;
     private final ConfigurationController mConfigurationController;
+    private final KeyguardViewController mKeyguardViewController;
+    private final FalsingManager mFalsingManager;
 
     private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
 
+    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+        private MotionEvent mTouchDown;
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            return false;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            // Do just a bit of our own falsing. People should only be tapping on the input, not
+            // swiping.
+            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                if (mTouchDown != null) {
+                    mTouchDown.recycle();
+                    mTouchDown = null;
+                }
+                mTouchDown = MotionEvent.obtain(ev);
+            } else if (mTouchDown != null) {
+                boolean tapResult = mFalsingManager.isFalseTap(true, 0.6);
+                if (tapResult
+                        || ev.getActionMasked() == MotionEvent.ACTION_UP
+                        || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+                    if (tapResult) {
+                        mKeyguardViewController.reset(true);
+                    }
+                    mTouchDown.recycle();
+                    mTouchDown = null;
+                }
+            }
+            return false;
+        }
+    };
+
     private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
         public void userActivity() {
             if (mSecurityCallback != null) {
@@ -169,7 +207,9 @@
             KeyguardStateController keyguardStateController,
             SecurityCallback securityCallback,
             KeyguardSecurityViewFlipperController securityViewFlipperController,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            KeyguardViewController keyguardViewController,
+            FalsingManager falsingManager) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -182,6 +222,8 @@
         mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
                 mKeyguardSecurityCallback);
         mConfigurationController = configurationController;
+        mKeyguardViewController = keyguardViewController;
+        mFalsingManager = falsingManager;
     }
 
     @Override
@@ -192,12 +234,14 @@
     @Override
     protected void onViewAttached() {
         mView.setSwipeListener(mSwipeListener);
+        mView.addMotionEventListener(mGlobalTouchListener);
         mConfigurationController.addCallback(mConfigurationListener);
     }
 
     @Override
     protected void onViewDetached() {
         mConfigurationController.removeCallback(mConfigurationListener);
+        mView.removeMotionEventListener(mGlobalTouchListener);
     }
 
     /** */
@@ -479,6 +523,8 @@
         private final KeyguardStateController mKeyguardStateController;
         private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
         private final ConfigurationController mConfigurationController;
+        private final KeyguardViewController mKeyguardViewController;
+        private final FalsingManager mFalsingManager;
 
         @Inject
         Factory(KeyguardSecurityContainer view,
@@ -491,7 +537,9 @@
                 UiEventLogger uiEventLogger,
                 KeyguardStateController keyguardStateController,
                 KeyguardSecurityViewFlipperController securityViewFlipperController,
-                ConfigurationController configurationController) {
+                ConfigurationController configurationController,
+                KeyguardViewController keyguardViewController,
+                FalsingManager falsingManager) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
             mLockPatternUtils = lockPatternUtils;
@@ -502,6 +550,8 @@
             mKeyguardStateController = keyguardStateController;
             mSecurityViewFlipperController = securityViewFlipperController;
             mConfigurationController = configurationController;
+            mKeyguardViewController = keyguardViewController;
+            mFalsingManager = falsingManager;
         }
 
         public KeyguardSecurityContainerController create(
@@ -510,7 +560,7 @@
                     mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
-                    mConfigurationController);
+                    mConfigurationController, mKeyguardViewController, mFalsingManager);
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 75ef4b32..e01e17d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -87,10 +87,10 @@
       * Translate the entire view, and optionally inform the wrapped view of the progress
       * so it can animate with the parent.
       */
-    public void animateForIme(int translationY, float interpolatedFraction) {
+    public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
         super.setTranslationY(translationY);
         KeyguardInputView v = getSecurityView();
-        if (v != null) v.animateForIme(interpolatedFraction);
+        if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index cdbbfe6..b2bf2e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -78,8 +78,8 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager,
-            FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector,
+            boolean isNewLayoutEnabled) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 falsingCollector);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 8fff342..620db48 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -85,8 +85,8 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager,
-            FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector,
+            boolean isNewLayoutEnabled) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 falsingCollector);
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 97d6e97..14b9691 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -15,6 +15,7 @@
  */
 package com.android.keyguard;
 
+import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -34,7 +35,9 @@
  * Provides background color and radius animations for key pad buttons.
  */
 class NumPadAnimator {
-    private ValueAnimator mAnimator;
+    private AnimatorSet mAnimator;
+    private ValueAnimator mExpandAnimator;
+    private ValueAnimator mContractAnimator;
     private GradientDrawable mBackground;
     private RippleDrawable mRipple;
     private GradientDrawable mRippleMask;
@@ -55,18 +58,28 @@
         mMargin = context.getResources().getDimensionPixelSize(R.dimen.num_pad_key_margin);
 
         // Actual values will be updated later, usually during an onLayout() call
-        mAnimator = ValueAnimator.ofFloat(0f);
-        mAnimator.setDuration(100);
-        mAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-        mAnimator.setRepeatMode(ValueAnimator.REVERSE);
-        mAnimator.setRepeatCount(1);
-        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+        mAnimator = new AnimatorSet();
+        mExpandAnimator = ValueAnimator.ofFloat(0f, 1f);
+        mExpandAnimator.setDuration(50);
+        mExpandAnimator.setInterpolator(Interpolators.LINEAR);
+        mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator anim) {
                     mBackground.setCornerRadius((float) anim.getAnimatedValue());
                     mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
                 }
         });
 
+        mContractAnimator = ValueAnimator.ofFloat(1f, 0f);
+        mContractAnimator.setStartDelay(33);
+        mContractAnimator.setDuration(417);
+        mContractAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mContractAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                public void onAnimationUpdate(ValueAnimator anim) {
+                    mBackground.setCornerRadius((float) anim.getAnimatedValue());
+                    mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
+                }
+        });
+        mAnimator.playSequentially(mExpandAnimator, mContractAnimator);
     }
 
     void updateMargin(ViewGroup.MarginLayoutParams lp) {
@@ -77,7 +90,8 @@
         float startRadius = height / 2f;
         float endRadius = height / 4f;
         mBackground.setCornerRadius(startRadius);
-        mAnimator.setFloatValues(startRadius, endRadius);
+        mExpandAnimator.setFloatValues(startRadius, endRadius);
+        mContractAnimator.setFloatValues(endRadius, startRadius);
     }
 
     void start() {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 5db3349..1635c49 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.VectorDrawable;
 import android.util.AttributeSet;
@@ -24,6 +25,8 @@
 import android.view.MotionEvent;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
@@ -32,13 +35,19 @@
  */
 public class NumPadButton extends AlphaOptimizedImageButton {
 
+    @Nullable
     private NumPadAnimator mAnimator;
 
     public NumPadButton(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
-                attrs.getStyleAttribute());
+        Drawable background = getBackground();
+        if (background instanceof LayerDrawable) {
+            mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
+                    attrs.getStyleAttribute());
+        } else {
+            mAnimator = null;
+        }
     }
 
     @Override
@@ -93,9 +102,11 @@
      * By default, the new layout will be enabled. Invoking will revert to the old style
      */
     public void disableNewLayout() {
-        mAnimator = null;
-        ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
-        setBackground(getContext().getResources().getDrawable(
-                R.drawable.ripple_drawable_pin, ctw.getTheme()));
+        if (mAnimator != null) {
+            mAnimator = null;
+            ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
+            setBackground(getContext().getResources().getDrawable(
+                    R.drawable.ripple_drawable_pin, ctw.getTheme()));
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index e6a9d4f..96fceee 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -31,6 +32,8 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
@@ -48,6 +51,7 @@
     private int mTextViewResId;
     private PasswordTextView mTextView;
 
+    @Nullable
     private NumPadAnimator mAnimator;
 
     private View.OnClickListener mListener = new View.OnClickListener() {
@@ -127,8 +131,13 @@
 
         setContentDescription(mDigitText.getText().toString());
 
-        mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
-                R.style.NumPadKey);
+        Drawable background = getBackground();
+        if (background instanceof LayerDrawable) {
+            mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
+                    R.style.NumPadKey);
+        } else {
+            mAnimator = null;
+        }
     }
 
     /**
@@ -136,10 +145,12 @@
      */
     public void disableNewLayout() {
         findViewById(R.id.klondike_text).setVisibility(View.VISIBLE);
-        mAnimator = null;
-        ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
-        setBackground(getContext().getResources().getDrawable(
-                R.drawable.ripple_drawable_pin, ctw.getTheme()));
+        if (mAnimator != null) {
+            mAnimator = null;
+            ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
+            setBackground(getContext().getResources().getDrawable(
+                    R.drawable.ripple_drawable_pin, ctw.getTheme()));
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 5ffc283..b80f8bd 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -121,9 +121,19 @@
     public PasswordTextView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        setFocusableInTouchMode(true);
-        setFocusable(true);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
+        TypedArray a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
+        try {
+            // If defined, use the provided values. If not, set them to true by default.
+            boolean isFocusable = a.getBoolean(android.R.styleable.View_focusable,
+                    /* defValue= */ true);
+            boolean isFocusableInTouchMode = a.getBoolean(
+                    android.R.styleable.View_focusableInTouchMode, /* defValue= */ true);
+            setFocusable(isFocusable);
+            setFocusableInTouchMode(isFocusableInTouchMode);
+        } finally {
+            a.recycle();
+        }
+        a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
         try {
             mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0);
             mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 1bf7474..c051b69 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -53,12 +53,18 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
+import com.android.systemui.util.Assert;
 
 import java.util.Locale;
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Class to register system actions with accessibility framework.
  */
@@ -128,23 +134,39 @@
     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT =
             AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT; // 13
 
+    public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
+            AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15
+
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
-    private SystemActionsBroadcastReceiver mReceiver;
+    private final SystemActionsBroadcastReceiver mReceiver;
     private Locale mLocale;
-    private AccessibilityManager mA11yManager;
+    private final AccessibilityManager mA11yManager;
+    private final Lazy<StatusBar> mStatusBar;
+    private final NotificationShadeWindowController mNotificationShadeController;
+    private final StatusBarWindowCallback mNotificationShadeCallback;
+    private boolean mDismissNotificationShadeActionRegistered;
 
     @Inject
-    public SystemActions(Context context) {
+    public SystemActions(Context context,
+            NotificationShadeWindowController notificationShadeController,
+            Lazy<StatusBar> statusBar) {
         super(context);
         mReceiver = new SystemActionsBroadcastReceiver();
         mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
         mA11yManager = (AccessibilityManager) mContext.getSystemService(
                 Context.ACCESSIBILITY_SERVICE);
+        mNotificationShadeController = notificationShadeController;
+        // Saving in instance variable since to prevent GC since
+        // NotificationShadeWindowController.registerCallback() only keeps weak references.
+        mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) ->
+                registerOrUnregisterDismissNotificationShadeAction();
+        mStatusBar = statusBar;
     }
 
     @Override
     public void start() {
+        mNotificationShadeController.registerCallback(mNotificationShadeCallback);
         mContext.registerReceiverForAllUsers(
                 mReceiver,
                 mReceiver.createIntentFilter(),
@@ -210,6 +232,32 @@
         mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
         mA11yManager.registerSystemAction(
                 actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT);
+        registerOrUnregisterDismissNotificationShadeAction();
+    }
+
+    private void registerOrUnregisterDismissNotificationShadeAction() {
+        Assert.isMainThread();
+
+        // Saving state in instance variable since this callback is called quite often to avoid
+        // binder calls
+        StatusBar statusBar = mStatusBar.get();
+        if (statusBar.isPanelExpanded() && !statusBar.isKeyguardShowing()) {
+            if (!mDismissNotificationShadeActionRegistered) {
+                mA11yManager.registerSystemAction(
+                        createRemoteAction(
+                                R.string.accessibility_system_action_dismiss_notification_shade,
+                                SystemActionsBroadcastReceiver
+                                        .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE),
+                        SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+                mDismissNotificationShadeActionRegistered = true;
+            }
+        } else {
+            if (mDismissNotificationShadeActionRegistered) {
+                mA11yManager.unregisterSystemAction(
+                        SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+                mDismissNotificationShadeActionRegistered = false;
+            }
+        }
     }
 
     /**
@@ -261,10 +309,15 @@
                         R.string.accessibility_system_action_on_screen_a11y_shortcut_chooser_label;
                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER;
                 break;
-            case  SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
+            case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT:
                 labelId = R.string.accessibility_system_action_hardware_a11y_shortcut_label;
                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT;
                 break;
+            case SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
+                labelId = R.string.accessibility_system_action_dismiss_notification_shade;
+                intent = SystemActionsBroadcastReceiver
+                        .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE;
+                break;
             default:
                 return;
         }
@@ -369,6 +422,10 @@
         mA11yManager.performAccessibilityShortcut();
     }
 
+    private void handleAccessibilityDismissNotificationShade() {
+        mStatusBar.get().animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, false /* force */);
+    }
+
     private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
         private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
         private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -384,6 +441,8 @@
                 "SYSTEM_ACTION_ACCESSIBILITY_BUTTON_MENU";
         private static final String INTENT_ACTION_ACCESSIBILITY_SHORTCUT =
                 "SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT";
+        private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
+                "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE";
 
         private PendingIntent createPendingIntent(Context context, String intentAction) {
             switch (intentAction) {
@@ -397,7 +456,8 @@
                 case INTENT_ACTION_TAKE_SCREENSHOT:
                 case INTENT_ACTION_ACCESSIBILITY_BUTTON:
                 case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER:
-                case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: {
+                case INTENT_ACTION_ACCESSIBILITY_SHORTCUT:
+                case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
                     Intent intent = new Intent(intentAction);
                     intent.setPackage(context.getPackageName());
                     return PendingIntent.getBroadcast(context, 0, intent,
@@ -422,6 +482,7 @@
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
+            intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
             return intentFilter;
         }
 
@@ -473,6 +534,10 @@
                     handleAccessibilityShortcut();
                     break;
                 }
+                case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
+                    handleAccessibilityDismissNotificationShade();
+                    break;
+                }
                 default:
                     break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 2040347..e53f5c9 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -20,7 +20,6 @@
 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
 
-import android.Manifest;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -43,6 +42,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.systemui.Dumpable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
@@ -370,13 +370,9 @@
     }
 
     // TODO ntmyren: remove after teamfood is finished
-    private boolean shouldShowAppPredictor(String pkgName) {
-        if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
-                false)) {
-            return false;
-        }
-        return mPackageManager.checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, pkgName)
-                == PackageManager.PERMISSION_GRANTED;
+    private boolean showSystemApps() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
     }
 
     /**
@@ -399,8 +395,8 @@
             return true;
         }
         // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
-        if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
-                || shouldShowAppPredictor(packageName)
+        if (((showSystemApps() && !packageName.equals("android"))
+                || appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
                 || isSpeechRecognizerUsage(appOpCode, packageName)) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 43ecf67..2036150 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -16,57 +16,63 @@
 
 package com.android.systemui.biometrics;
 
-import android.annotation.NonNull;
 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;
 
-import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.statusbar.phone.StatusBar;
-
 /**
  * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
- * can support multiple child views drawing on the same region around the sensor location.
+ * can support multiple child views drawing in the same region around the sensor location.
+ *
+ * - hides animation view when pausing auth
+ * - sends illumination events to fingerprint drawable
+ * - sends sensor rect updates to fingerprint drawable
+ * - optionally can override dozeTimeTick to adjust views for burn-in mitigation
  */
-public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver,
-        StatusBar.ExpansionChangedListener {
+abstract class UdfpsAnimationView extends FrameLayout {
 
-    private static final String TAG = "UdfpsAnimationView";
-
-    @Nullable protected abstract UdfpsAnimation getUdfpsAnimation();
-
-    @NonNull private UdfpsView mParent;
-    @NonNull private RectF mSensorRect;
     private int mAlpha;
+    private boolean mPauseAuth;
 
     public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
-        mSensorRect = new RectF();
-        setWillNotDraw(false);
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
+    /**
+     * Fingerprint drawable
+     */
+    abstract UdfpsDrawable getDrawable();
 
-        if (getUdfpsAnimation() != null) {
-            final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
-            getUdfpsAnimation().setAlpha(alpha);
-            getUdfpsAnimation().draw(canvas);
-        }
+    void onSensorRectUpdated(RectF bounds) {
+        getDrawable().onSensorRectUpdated(bounds);
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
+    void onIlluminationStarting() {
+        getDrawable().setIlluminationShowing(true);
+        getDrawable().invalidateSelf();
+    }
 
-        if (getUdfpsAnimation() != null) {
-            getUdfpsAnimation().onDestroy();
+    void onIlluminationStopped() {
+        getDrawable().setIlluminationShowing(false);
+        getDrawable().invalidateSelf();
+    }
+
+    /**
+     * @return true if changed
+     */
+    boolean setPauseAuth(boolean pauseAuth) {
+        if (pauseAuth != mPauseAuth) {
+            mPauseAuth = pauseAuth;
+            updateAlpha();
+            return true;
         }
+        return false;
+    }
+
+    private void updateAlpha() {
+        getDrawable().setAlpha(mPauseAuth ? mAlpha : 255);
     }
 
     private int expansionToAlpha(float expansion) {
@@ -81,76 +87,15 @@
         return (int) ((1 - percent) * 255);
     }
 
-    void onIlluminationStarting() {
-        if (getUdfpsAnimation() == null) {
-            return;
-        }
-
-        getUdfpsAnimation().setIlluminationShowing(true);
-        postInvalidate();
-    }
-
-    void onIlluminationStopped() {
-        if (getUdfpsAnimation() == null) {
-            return;
-        }
-
-        getUdfpsAnimation().setIlluminationShowing(false);
-        postInvalidate();
-    }
-
-    void setParent(@NonNull UdfpsView parent) {
-        mParent = parent;
-    }
-
-    void onSensorRectUpdated(@NonNull RectF sensorRect) {
-        mSensorRect = sensorRect;
-        if (getUdfpsAnimation() != null) {
-            getUdfpsAnimation().onSensorRectUpdated(mSensorRect);
-        }
-    }
-
-    void updateColor() {
-        if (getUdfpsAnimation() != null) {
-            getUdfpsAnimation().updateColor();
-        }
-        postInvalidate();
-    }
-
-    @Override
-    public void dozeTimeTick() {
-        if (getUdfpsAnimation() instanceof DozeReceiver) {
-            ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick();
-        }
-    }
-
-    @Override
     public void onExpansionChanged(float expansion, boolean expanded) {
         mAlpha = expansionToAlpha(expansion);
-        postInvalidate();
-    }
-
-    public int getPaddingX() {
-        if (getUdfpsAnimation() == null) {
-            return 0;
-        }
-        return getUdfpsAnimation().getPaddingX();
-    }
-
-    public int getPaddingY() {
-        if (getUdfpsAnimation() == null) {
-            return 0;
-        }
-        return getUdfpsAnimation().getPaddingY();
+        updateAlpha();
     }
 
     /**
-     * @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.
+     * @return true if handled
      */
-    @NonNull
-    PointF getTouchTranslation() {
-        return new PointF(0, 0);
+    boolean dozeTimeTick() {
+        return false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
new file mode 100644
index 0000000..b6d80ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -0,0 +1,167 @@
+/*
+ * 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 static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.ViewController;
+
+/**
+ * Handles:
+ * 1. registering for listeners when its view is attached and unregistering on view detached
+ * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
+ * 3. sending events to its view including:
+ *      - illumination events
+ *      - sensor position changes
+ *      - doze time event
+ */
+abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
+        extends ViewController<T> {
+    @NonNull final StatusBarStateController mStatusBarStateController;
+    @NonNull final StatusBar mStatusBar;
+
+    private boolean mNotificationShadeExpanded;
+    private int mStatusBarState;
+
+    protected UdfpsAnimationViewController(
+            T view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view);
+        mStatusBarStateController = statusBarStateController;
+        mStatusBar = statusBar;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mStatusBarStateController.addCallback(mStateListener);
+        mStateListener.onStateChanged(mStatusBarStateController.getState());
+        mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mStatusBarStateController.removeCallback(mStateListener);
+        mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+    }
+
+    /**
+     * Returns true if the fingerprint manager is running but we want to temporarily pause
+     * authentication.
+     */
+    boolean shouldPauseAuth() {
+        return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
+                || mStatusBarState == SHADE_LOCKED
+                || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+    }
+
+    /**
+     * Send pause auth update to our view.
+     */
+    void updatePauseAuth() {
+        if (mView.setPauseAuth(shouldPauseAuth())) {
+            mView.postInvalidate();
+        }
+    }
+
+    /**
+     * Send sensor position change to our view. This rect contains paddingX and paddingY.
+     */
+    void onSensorRectUpdated(RectF sensorRect) {
+        mView.onSensorRectUpdated(sensorRect);
+    }
+
+    /**
+     * Send dozeTimeTick to view in case it wants to handle its burn-in offset.
+     */
+    void dozeTimeTick() {
+        if (mView.dozeTimeTick()) {
+            mView.postInvalidate();
+        }
+    }
+
+    /**
+     * @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.
+     */
+    PointF getTouchTranslation() {
+        return new PointF(0, 0);
+    }
+
+    /**
+     * X-Padding to add to left and right of the sensor rectangle area to increase the size of our
+     * window to draw within.
+     * @return
+     */
+    int getPaddingX() {
+        return 0;
+    }
+
+    /**
+     * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our
+     * window to draw within.
+     */
+    int getPaddingY() {
+        return 0;
+    }
+
+    /**
+     * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+     */
+    void onIlluminationStarting() {
+        mView.onIlluminationStarting();
+        mView.postInvalidate();
+    }
+
+    /**
+     * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
+     * authenticate.
+     */
+    void onIlluminationStopped() {
+        mView.onIlluminationStopped();
+        mView.postInvalidate();
+    }
+
+    private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
+            new StatusBar.ExpansionChangedListener() {
+                @Override
+                public void onExpansionChanged(float expansion, boolean expanded) {
+                    mNotificationShadeExpanded = expanded;
+                    mView.onExpansionChanged(expansion, expanded);
+                    updatePauseAuth();
+                }
+            };
+
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    mStatusBarState = newState;
+                    updatePauseAuth();
+                }
+            };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
deleted file mode 100644
index 543df33..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.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;
-
-import com.android.systemui.R;
-
-/**
- * Class that coordinates non-HBM animations during enrollment.
- */
-public class UdfpsAnimationViewEnroll extends UdfpsAnimationView
-        implements UdfpsEnrollHelper.Listener {
-
-    private static final String TAG = "UdfpsAnimationViewEnroll";
-
-    @NonNull private UdfpsAnimationEnroll mUdfpsAnimation;
-    @NonNull private UdfpsProgressBar mProgressBar;
-    @Nullable private UdfpsEnrollHelper mEnrollHelper;
-
-    @NonNull
-    @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return mUdfpsAnimation;
-    }
-
-    public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mUdfpsAnimation = new UdfpsAnimationEnroll(context);
-    }
-
-    public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
-        mEnrollHelper = helper;
-        mUdfpsAnimation.setEnrollHelper(helper);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        mProgressBar = findViewById(R.id.progress_bar);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (mEnrollHelper == null) {
-            Log.e(TAG, "Enroll helper is null");
-            return;
-        }
-
-        if (mEnrollHelper.shouldShowProgressBar()) {
-            mProgressBar.setVisibility(View.VISIBLE);
-
-            // Only need enrollment updates if the progress bar is showing :)
-            mEnrollHelper.setListener(this);
-        }
-    }
-
-    @Override
-    public void onEnrollmentProgress(int remaining, int totalSteps) {
-        final int interpolatedProgress = mProgressBar.getMax()
-                * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
-
-        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/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
deleted file mode 100644
index 3d2f5a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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 other usage of FingerprintManager (not
- * including Keyguard).
- */
-public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView {
-
-    private final UdfpsAnimationFpmOther mAnimation;
-
-    public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mAnimation = new UdfpsAnimationFpmOther(context);
-    }
-
-    @Nullable
-    @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return mAnimation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
deleted file mode 100644
index 7d0b3e5..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-
-/**
- * Class that coordinates non-HBM animations during keyguard authentication.
- */
-public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView {
-    @Nullable private UdfpsAnimationKeyguard mAnimation;
-
-    public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) {
-        if (mAnimation == null) {
-            mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController);
-            mAnimation.setAnimationView(this);
-        }
-    }
-
-    @Nullable
-    @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return mAnimation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
index 515b442..70be907 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
@@ -24,19 +24,22 @@
 /**
  * Class that coordinates non-HBM animations during BiometricPrompt.
  *
+ * Currently doesn't draw anything.
+ *
  * 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).
+ * de-dupe this if necessary.
  */
-public class UdfpsAnimationViewBp extends UdfpsAnimationView {
-    public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) {
+public class UdfpsBpView extends UdfpsAnimationView {
+    private UdfpsFpDrawable mFingerprintDrawable;
+
+    public UdfpsBpView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
+        // Drawable isn't ever added to the view, so we don't currently show anything
+        mFingerprintDrawable = new UdfpsFpDrawable(mContext);
     }
 
-    @Nullable
     @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return null;
+    UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
new file mode 100644
index 0000000..b712c65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -0,0 +1,32 @@
+/*
+ * 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 com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for biometric prompt.
+ */
+class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
+    protected UdfpsBpViewController(
+            UdfpsBpView view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 4b6a8f6..94aeb73 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -31,9 +31,9 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.SystemClock;
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -195,17 +195,6 @@
         }
     }
 
-    @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
-            (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);
-
-    @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onStateChanged(int newState) {
-                    mView.onStateChanged(newState);
-                }
-            };
-
     private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
         final float vx = tracker.getXVelocity(pointerId);
         final float vy = tracker.getYVelocity(pointerId);
@@ -360,10 +349,9 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mView == null) {
-            return;
+        if (mView != null) {
+            mView.dozeTimeTick();
         }
-        mView.dozeTimeTick();
     }
 
     /**
@@ -387,7 +375,8 @@
         }
     }
 
-    private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) {
+    private WindowManager.LayoutParams computeLayoutParams(
+            @Nullable UdfpsAnimationViewController animation) {
         final int paddingX = animation != null ? animation.getPaddingX() : 0;
         final int paddingY = animation != null ? animation.getPaddingY() : 0;
 
@@ -438,20 +427,13 @@
         mFgExecutor.execute(() -> {
             if (mView == null) {
                 try {
-                    Log.v(TAG, "showUdfpsOverlay | adding window");
-                    // TODO: Eventually we should refactor the code to inflate an
-                    //  operation-specific view here, instead of inflating a generic udfps_view
-                    //  and adding operation-specific animations to it.
+                    Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
                     mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
                     mView.setSensorProperties(mSensorProps);
                     mView.setHbmCallback(this);
-
-                    final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason);
-                    mView.setAnimationView(animation);
-
-                    mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener);
-                    mStatusBarStateController.addCallback(mStatusBarStateListener);
-                    mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+                    UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
+                    animation.init();
+                    mView.setAnimationViewController(animation);
 
                     mWindowManager.addView(mView, computeLayoutParams(animation));
                     mView.setOnTouchListener(mOnTouchListener);
@@ -464,40 +446,46 @@
         });
     }
 
-    @NonNull
-    private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) {
-        Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
-
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-
+    private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
         switch (reason) {
             case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
-            case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
-                final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll)
-                        inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
-                view.setEnrollHelper(mServerRequest.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 view = (UdfpsAnimationViewKeyguard)
-                        inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
-                view.setStatusBarStateController(mStatusBarStateController);
-                return view;
-            }
-
-            case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
-                final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther)
-                        inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
-                return view;
-            }
-
+            case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
+                UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate(
+                        R.layout.udfps_enroll_view, null);
+                mView.addView(enrollView);
+                return new UdfpsEnrollViewController(
+                        enrollView,
+                        mServerRequest.mEnrollHelper,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
+            case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
+                UdfpsKeyguardView keyguardView = (UdfpsKeyguardView)
+                        mInflater.inflate(R.layout.udfps_keyguard_view, null);
+                mView.addView(keyguardView);
+                return new UdfpsKeyguardViewController(
+                        keyguardView,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
+            case IUdfpsOverlayController.REASON_AUTH_BP:
+                // note: empty controller, currently shows no visual affordance
+                UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null);
+                mView.addView(bpView);
+                return new UdfpsBpViewController(
+                        bpView,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
+            case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
+                UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
+                        mInflater.inflate(R.layout.udfps_fpm_other_view, null);
+                mView.addView(authOtherView);
+                return new UdfpsFpmOtherViewController(
+                        authOtherView,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
             default:
                 Log.d(TAG, "Animation for reason " + reason + " not supported yet");
                 return null;
@@ -510,11 +498,9 @@
                 Log.v(TAG, "hideUdfpsOverlay | removing window");
                 // Reset the controller back to its starting state.
                 onFingerUp();
-
-                mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener);
-                mStatusBarStateController.removeCallback(mStatusBarStateListener);
-
                 mWindowManager.removeView(mView);
+                mView.setOnTouchListener(null);
+                mView.setAnimationViewController(null);
                 mView = null;
             } else {
                 Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
index a51b6fd1..13d31cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
@@ -17,10 +17,10 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
+import android.graphics.ColorFilter;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -28,24 +28,24 @@
 import com.android.systemui.R;
 
 /**
- * Abstract base class for animations that should be drawn when the finger is not touching the
+ * Abstract base class for drawable displayed when the finger is not touching the
  * sensor area.
  */
-public abstract class UdfpsAnimation extends Drawable {
-    protected abstract void updateColor();
-    protected abstract void onDestroy();
-
+public abstract class UdfpsDrawable extends Drawable {
     @NonNull protected final Context mContext;
     @NonNull protected final Drawable mFingerprintDrawable;
-    @Nullable private View mView;
     private boolean mIlluminationShowing;
 
-    public UdfpsAnimation(@NonNull Context context) {
+    int mAlpha = 255; // 0 - 255
+    public UdfpsDrawable(@NonNull Context context) {
         mContext = context;
         mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
         mFingerprintDrawable.mutate();
     }
 
+    /**
+     * @param sensorRect the rect coordinates for the sensor area
+     */
     public void onSensorRectUpdated(@NonNull RectF sensorRect) {
         final int margin =  (int) sensorRect.height() / 8;
 
@@ -56,17 +56,17 @@
         updateFingerprintIconBounds(bounds);
     }
 
+    /**
+     * Bounds for the fingerprint icon
+     */
     protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
         mFingerprintDrawable.setBounds(bounds);
     }
 
     @Override
     public void setAlpha(int alpha) {
-        mFingerprintDrawable.setAlpha(alpha);
-    }
-
-    public void setAnimationView(UdfpsAnimationView view) {
-        mView = view;
+        mAlpha = alpha;
+        mFingerprintDrawable.setAlpha(mAlpha);
     }
 
     boolean isIlluminationShowing() {
@@ -77,23 +77,12 @@
         mIlluminationShowing = showing;
     }
 
-    /**
-     * @return The amount of padding that's needed on each side of the sensor, in pixels.
-     */
-    public int getPaddingX() {
-        return 0;
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
     }
 
-    /**
-     * @return The amount of padding that's needed on each side of the sensor, in pixels.
-     */
-    public int getPaddingY() {
+    @Override
+    public int getOpacity() {
         return 0;
     }
-
-    protected void postInvalidateView() {
-        if (mView != null) {
-            mView.postInvalidate();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 015a598..d80e085 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,7 +20,6 @@
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -33,23 +32,23 @@
 import com.android.systemui.R;
 
 /**
- * UDFPS animations that should be shown when enrolling.
+ * UDFPS fingerprint drawable that is shown when enrolling
  */
-public class UdfpsAnimationEnroll extends UdfpsAnimation {
+public class UdfpsEnrollDrawable extends UdfpsDrawable {
     private static final String TAG = "UdfpsAnimationEnroll";
 
     private static final float SHADOW_RADIUS = 5.f;
-    private static final float PROGRESS_BAR_RADIUS = 140.f;
+    static final float PROGRESS_BAR_RADIUS = 140.f;
 
     @NonNull private final Drawable mMovingTargetFpIcon;
     @NonNull private final Paint mSensorPaint;
     @NonNull private final Paint mBlueFill;
-    @NonNull private final Paint mBlueStroke;;
+    @NonNull private final Paint mBlueStroke;
 
     @Nullable private RectF mSensorRect;
     @Nullable private UdfpsEnrollHelper mEnrollHelper;
 
-    UdfpsAnimationEnroll(@NonNull Context context) {
+    UdfpsEnrollDrawable(@NonNull Context context) {
         super(context);
 
         mSensorPaint = new Paint(0 /* flags */);
@@ -72,6 +71,8 @@
         mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
         mMovingTargetFpIcon.setTint(Color.WHITE);
         mMovingTargetFpIcon.mutate();
+
+        mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
     }
 
     void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
@@ -79,16 +80,6 @@
     }
 
     @Override
-    protected void updateColor() {
-        mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
-    }
-
-    @Override
-    protected void onDestroy() {
-
-    }
-
-    @Override
     public void onSensorRectUpdated(@NonNull RectF sensorRect) {
         super.onSensorRectUpdated(sensorRect);
         mSensorRect = sensorRect;
@@ -98,6 +89,7 @@
     protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
         super.updateFingerprintIconBounds(bounds);
         mMovingTargetFpIcon.setBounds(bounds);
+        invalidateSelf();
     }
 
     @Override
@@ -117,7 +109,7 @@
 
         // Draw moving target
         if (mEnrollHelper.isCenterEnrollmentComplete()) {
-            mFingerprintDrawable.setAlpha(64);
+            mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
 
             canvas.save();
             final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
@@ -130,33 +122,16 @@
             mMovingTargetFpIcon.draw(canvas);
             canvas.restore();
         } else {
-            mFingerprintDrawable.setAlpha(255);
+            mFingerprintDrawable.setAlpha(mAlpha);
         }
     }
 
     @Override
-    public int getPaddingX() {
-        return (int) Math.ceil(PROGRESS_BAR_RADIUS);
-    }
-
-    @Override
-    public int getPaddingY() {
-        return (int) Math.ceil(PROGRESS_BAR_RADIUS);
-    }
-
-    @Override
     public void setAlpha(int alpha) {
         super.setAlpha(alpha);
         mSensorPaint.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return 0;
+        mBlueFill.setAlpha(alpha);
+        mBlueStroke.setAlpha(alpha);
+        invalidateSelf();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 667b7a7..98a703f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -21,6 +21,9 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.TypedValue;
 
 import java.util.ArrayList;
@@ -32,6 +35,10 @@
 public class UdfpsEnrollHelper {
     private static final String TAG = "UdfpsEnrollHelper";
 
+    private static final String SCALE_OVERRIDE =
+            "com.android.systemui.biometrics.UdfpsEnrollHelper.scale";
+    private static final float SCALE = 0.5f;
+
     // Enroll with two center touches before going to guided enrollment
     private static final int NUM_CENTER_TOUCHES = 2;
 
@@ -39,9 +46,10 @@
         void onEnrollmentProgress(int remaining, int totalSteps);
     }
 
+    @NonNull private final Context mContext;
     // IUdfpsOverlayController reason
     private final int mEnrollReason;
-    private final List<PointF> mGuidedEnrollmentPoints;
+    @NonNull private final List<PointF> mGuidedEnrollmentPoints;
 
     private int mTotalSteps = -1;
     private int mRemainingSteps = -1;
@@ -53,6 +61,7 @@
     @Nullable Listener mListener;
 
     public UdfpsEnrollHelper(@NonNull Context context, int reason) {
+        mContext = context;
         mEnrollReason = reason;
         mGuidedEnrollmentPoints = new ArrayList<>();
 
@@ -100,13 +109,13 @@
 
     }
 
-    void setListener(@NonNull Listener listener) {
+    void setListener(Listener listener) {
         mListener = listener;
 
         // Only notify during setListener if enrollment is already in progress, so the progress
         // bar can be updated. If enrollment has not started yet, the progress bar will be empty
         // anyway.
-        if (mTotalSteps != -1) {
+        if (mListener != null && mTotalSteps != -1) {
             mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
         }
     }
@@ -121,9 +130,15 @@
 
     @NonNull
     PointF getNextGuidedEnrollmentPoint() {
+        float scale = SCALE;
+        if (Build.IS_ENG || Build.IS_USERDEBUG) {
+            scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+                    SCALE_OVERRIDE, SCALE,
+                    UserHandle.USER_CURRENT);
+        }
         final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
         final PointF originalPoint = mGuidedEnrollmentPoints
                 .get(index % mGuidedEnrollmentPoints.size());
-        return new PointF(originalPoint.x * 0.5f, originalPoint.y * 0.5f);
+        return new PointF(originalPoint.x * scale, originalPoint.y * scale);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
new file mode 100644
index 0000000..7985d95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -0,0 +1,53 @@
+/*
+ * 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 android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_enroll_view.xml
+ */
+public class UdfpsEnrollView extends UdfpsAnimationView {
+    private final UdfpsEnrollDrawable mFingerprintDrawable;
+    private ImageView mFingerprintView;
+
+    public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mFingerprintDrawable = new UdfpsEnrollDrawable(mContext);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view);
+        mFingerprintView.setImageDrawable(mFingerprintDrawable);
+    }
+
+    @Override
+    public UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
+    }
+
+    void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
+        mFingerprintDrawable.setEnrollHelper(enrollHelper);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
new file mode 100644
index 0000000..da8d712
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.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 com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during enrollment.
+ */
+public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> {
+    @NonNull private final UdfpsProgressBar mProgressBar;
+    @NonNull private final UdfpsEnrollHelper mEnrollHelper;
+
+    protected UdfpsEnrollViewController(
+            UdfpsEnrollView view,
+            @NonNull UdfpsEnrollHelper enrollHelper,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+        mEnrollHelper = enrollHelper;
+        mProgressBar = mView.findViewById(R.id.progress_bar);
+        mView.setEnrollHelper(mEnrollHelper);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        super.onViewAttached();
+        if (mEnrollHelper.shouldShowProgressBar()) {
+            mProgressBar.setVisibility(View.VISIBLE);
+
+            // Only need enrollment updates if the progress bar is showing :)
+            mEnrollHelper.setListener(mEnrollHelperListener);
+        }
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mEnrollHelper.setListener(null);
+    }
+
+    @NonNull
+    @Override
+    public PointF getTouchTranslation() {
+        if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+            return new PointF(0, 0);
+        } else {
+            return mEnrollHelper.getNextGuidedEnrollmentPoint();
+        }
+    }
+
+    @Override
+    public int getPaddingX() {
+        return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+    }
+
+    @Override
+    public int getPaddingY() {
+        return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+    }
+
+    private final UdfpsEnrollHelper.Listener mEnrollHelperListener =
+            new UdfpsEnrollHelper.Listener() {
+        @Override
+        public void onEnrollmentProgress(int remaining, int totalSteps) {
+            final int interpolatedProgress = mProgressBar.getMax()
+                    * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
+
+            mProgressBar.setProgress(interpolatedProgress, true);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
index ef7a340..09b6fab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
@@ -18,32 +18,19 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.ColorFilter;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 /**
- * UDFPS animations that should be shown when authenticating via FingerprintManager, excluding
- * keyguard.
+ * Draws udfps fingerprint if sensor isn't illuminating.
  */
-public class UdfpsAnimationFpmOther extends UdfpsAnimation {
+public class UdfpsFpDrawable extends UdfpsDrawable {
 
-    UdfpsAnimationFpmOther(@NonNull Context context) {
+    UdfpsFpDrawable(@NonNull Context context) {
         super(context);
     }
 
     @Override
-    protected void updateColor() {
-
-    }
-
-    @Override
-    protected void onDestroy() {
-
-    }
-
-    @Override
     public void draw(@NonNull Canvas canvas) {
         if (isIlluminationShowing()) {
             return;
@@ -51,14 +38,4 @@
 
         mFingerprintDrawable.draw(canvas);
     }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return 0;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
new file mode 100644
index 0000000..85f1606
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_fpm_other_view.xml
+ */
+public class UdfpsFpmOtherView extends UdfpsAnimationView {
+    private final UdfpsFpDrawable mFingerprintDrawable;
+    private ImageView mFingerprintView;
+
+    public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mFingerprintDrawable = new UdfpsFpDrawable(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view);
+        mFingerprintView.setImageDrawable(mFingerprintDrawable);
+    }
+
+    @Override
+    UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
new file mode 100644
index 0000000..587501b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
+ * states.
+ *
+ * Currently only shows the fp drawable.
+ */
+class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> {
+    protected UdfpsFpmOtherViewController(
+            UdfpsFpmOtherView view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
index 5f268cf..b0c5da0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
@@ -21,28 +21,25 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.util.MathUtils;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.internal.graphics.ColorUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 
 /**
  * UDFPS animations that should be shown when authenticating on keyguard.
  */
-public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiver,
-        StatusBarStateController.StateListener {
+public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver {
 
     private static final String TAG = "UdfpsAnimationKeyguard";
+    private final int mLockScreenColor;
+    private final int mAmbientDisplayColor;
 
     @NonNull private final Context mContext;
-    @NonNull private final StatusBarStateController mStatusBarStateController;
     private final int mMaxBurnInOffsetX;
     private final int mMaxBurnInOffsetY;
 
@@ -51,18 +48,19 @@
     private float mBurnInOffsetX;
     private float mBurnInOffsetY;
 
-    UdfpsAnimationKeyguard(@NonNull Context context,
-            @NonNull StatusBarStateController statusBarStateController) {
+    UdfpsKeyguardDrawable(@NonNull Context context) {
         super(context);
         mContext = context;
-        mStatusBarStateController = statusBarStateController;
 
+        // TODO: move burn-in to view
         mMaxBurnInOffsetX = context.getResources()
                 .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
         mMaxBurnInOffsetY = context.getResources()
                 .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
 
-        statusBarStateController.addCallback(this);
+        mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+        mAmbientDisplayColor = Color.WHITE;
+        updateAodPositionAndColor();
     }
 
     private void updateAodPositionAndColor() {
@@ -74,8 +72,10 @@
                 getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
                         - mMaxBurnInOffsetY,
                 mInterpolatedDarkAmount);
-        updateColor();
-        postInvalidateView();
+
+        mFingerprintDrawable.setTint(ColorUtils.blendARGB(mLockScreenColor,
+                mAmbientDisplayColor, mInterpolatedDarkAmount));
+        invalidateSelf();
     }
 
     @Override
@@ -84,44 +84,15 @@
     }
 
     @Override
-    public void onDozeAmountChanged(float linear, float eased) {
-        mInterpolatedDarkAmount = eased;
-        updateAodPositionAndColor();
-    }
-
-    @Override
     public void draw(@NonNull Canvas canvas) {
         if (isIlluminationShowing()) {
             return;
         }
-
-        canvas.save();
-        canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
         mFingerprintDrawable.draw(canvas);
-        canvas.restore();
     }
 
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return 0;
-    }
-
-    @Override
-    protected void updateColor() {
-        final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
-                R.attr.wallpaperTextColor);
-        final int ambientDisplayIconColor = Color.WHITE;
-        mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
-                ambientDisplayIconColor, mInterpolatedDarkAmount));
-    }
-
-    @Override
-    protected void onDestroy() {
-        mStatusBarStateController.removeCallback(this);
+    void onDozeAmountChanged(float linear, float eased) {
+        mInterpolatedDarkAmount = eased;
+        updateAodPositionAndColor();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
new file mode 100644
index 0000000..6a93560
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_keyguard_view.xml
+ */
+public class UdfpsKeyguardView extends UdfpsAnimationView {
+    private final UdfpsKeyguardDrawable mFingerprintDrawable;
+    private ImageView mFingerprintView;
+
+    public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mFingerprintDrawable = new UdfpsKeyguardDrawable(mContext);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view);
+        mFingerprintView.setImageDrawable(mFingerprintDrawable);
+    }
+
+    @Override
+    public UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
+    }
+
+    @Override
+    public boolean dozeTimeTick() {
+        // TODO: burnin
+        mFingerprintDrawable.dozeTimeTick();
+        return true;
+    }
+
+    void onDozeAmountChanged(float linear, float eased) {
+        mFingerprintDrawable.onDozeAmountChanged(linear, eased);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
new file mode 100644
index 0000000..14bb3fee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -0,0 +1,81 @@
+/*
+ * 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 com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during keyguard authentication.
+ */
+public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
+    private boolean mForceShow;
+
+    protected UdfpsKeyguardViewController(
+            UdfpsKeyguardView view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        super.onViewAttached();
+        mStatusBarStateController.addCallback(mStateListener);
+        final float dozeAmount = mStatusBarStateController.getDozeAmount();
+        mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mStatusBarStateController.removeCallback(mStateListener);
+    }
+
+    /**
+     * Overrides non-force show logic in shouldPauseAuth to still auth.
+     */
+    private void forceShow(boolean forceShow) {
+        if (mForceShow == forceShow) {
+            return;
+        }
+
+        mForceShow = forceShow;
+        updatePauseAuth();
+        // TODO: animate show/hide background protection
+    }
+
+    /**
+     * Returns true if the fingerprint manager is running but we want to temporarily pause
+     * authentication. On the keyguard, we may want to show udfps when the shade
+     * is expanded, so this can be overridden with the forceShow method.
+     */
+    public boolean shouldPauseAuth() {
+        if (mForceShow) {
+            return false;
+        }
+        return super.shouldPauseAuth();
+    }
+
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+        @Override
+        public void onDozeAmountChanged(float linear, float eased) {
+            mView.onDozeAmountChanged(linear, eased);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index a52bddc..42d0d84 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.biometrics;
 
-import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -39,15 +35,12 @@
 
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
  * animations.
  */
-public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
-        StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
+public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator {
     private static final String TAG = "UdfpsView";
 
     private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -56,7 +49,7 @@
     @NonNull private final Paint mDebugTextPaint;
 
     @NonNull private UdfpsSurfaceView mHbmSurfaceView;
-    @Nullable private UdfpsAnimationView mAnimationView;
+    @Nullable private UdfpsAnimationViewController mAnimationViewController;
 
     // Used to obtain the sensor location.
     @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,8 +57,6 @@
     private final float mSensorTouchAreaCoefficient;
     @Nullable private String mDebugMessage;
     private boolean mIlluminationRequested;
-    private int mStatusBarState;
-    private boolean mNotificationShadeExpanded;
 
     public UdfpsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -108,15 +99,6 @@
         mSensorProps = properties;
     }
 
-    void setAnimationView(@NonNull UdfpsAnimationView animation) {
-        mAnimationView = animation;
-        animation.setParent(this);
-
-        // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it
-        //  after the animation type has been decided.
-        addView(animation, 0);
-    }
-
     @Override
     public void setHbmCallback(@Nullable HbmCallback callback) {
         mHbmSurfaceView.setHbmCallback(callback);
@@ -124,45 +106,38 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mAnimationView == null) {
-            return;
-        }
-        mAnimationView.dozeTimeTick();
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        mStatusBarState = newState;
-    }
-
-    @Override
-    public void onExpansionChanged(float expansion, boolean expanded) {
-        mNotificationShadeExpanded = expanded;
-
-        if (mAnimationView != null) {
-            mAnimationView.onExpansionChanged(expansion, expanded);
+        if (mAnimationViewController != null) {
+            mAnimationViewController.dozeTimeTick();
         }
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mSensorRect.set(0 + mAnimationView.getPaddingX(),
-                0 + mAnimationView.getPaddingY(),
-                2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
-                2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+        int paddingX = mAnimationViewController == null ? 0
+                : mAnimationViewController.getPaddingX();
+        int paddingY = mAnimationViewController == null ? 0
+                : mAnimationViewController.getPaddingY();
+        mSensorRect.set(
+                paddingX,
+                paddingY,
+                2 * mSensorProps.sensorRadius + paddingX,
+                2 * mSensorProps.sensorRadius + paddingY);
 
         mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
-        mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
+        if (mAnimationViewController != null) {
+            mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
+        }
+    }
+
+    void setAnimationViewController(UdfpsAnimationViewController animationViewController) {
+        mAnimationViewController = animationViewController;
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Log.v(TAG, "onAttachedToWindow");
-
-        // Retrieve the colors each time, since it depends on day/night mode
-        mAnimationView.updateColor();
     }
 
     @Override
@@ -188,7 +163,9 @@
 
     boolean isWithinSensorArea(float x, float y) {
         // The X and Y coordinates of the sensor's center.
-        final PointF translation = mAnimationView.getTouchTranslation();
+        final PointF translation = mAnimationViewController == null
+                ? new PointF(0, 0)
+                : mAnimationViewController.getTouchTranslation();
         final float cx = mSensorRect.centerX() + translation.x;
         final float cy = mSensorRect.centerY() + translation.y;
         // Radii along the X and Y axes.
@@ -199,18 +176,7 @@
                 && x < (cx + rx * mSensorTouchAreaCoefficient)
                 && y > (cy - ry * mSensorTouchAreaCoefficient)
                 && y < (cy + ry * mSensorTouchAreaCoefficient)
-                && !shouldPauseAuth();
-    }
-
-    /**
-     * States where UDFPS should temporarily not be authenticating. Instead of completely stopping
-     * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
-     * sending onFingerDown and smoothly animating away.
-     */
-    boolean shouldPauseAuth() {
-        return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
-                || mStatusBarState == SHADE_LOCKED
-                || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+                && !mAnimationViewController.shouldPauseAuth();
     }
 
     boolean isIlluminationRequested() {
@@ -223,7 +189,9 @@
     @Override
     public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
         mIlluminationRequested = true;
-        mAnimationView.onIlluminationStarting();
+        if (mAnimationViewController != null) {
+            mAnimationViewController.onIlluminationStarting();
+        }
         mHbmSurfaceView.setVisibility(View.VISIBLE);
         mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
     }
@@ -231,7 +199,9 @@
     @Override
     public void stopIllumination() {
         mIlluminationRequested = false;
-        mAnimationView.onIlluminationStopped();
+        if (mAnimationViewController != null) {
+            mAnimationViewController.onIlluminationStopped();
+        }
         mHbmSurfaceView.setVisibility(View.INVISIBLE);
         mHbmSurfaceView.stopIllumination();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 6572ca9..efb7992 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
 import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS;
-import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS;
 
 import android.net.Uri;
 import android.os.Build;
@@ -29,11 +28,9 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.TestHarness;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.ThresholdSensor;
 
 import java.io.FileDescriptor;
@@ -63,14 +60,13 @@
 
     private static final int RECENT_INFO_LOG_SIZE = 40;
     private static final int RECENT_SWIPE_LOG_SIZE = 20;
+    private static final double TAP_CONFIDENCE_THRESHOLD = 0.7;
 
     private final FalsingDataProvider mDataProvider;
     private final DockManager mDockManager;
     private final SingleTapClassifier mSingleTapClassifier;
     private final DoubleTapClassifier mDoubleTapClassifier;
     private final HistoryTracker mHistoryTracker;
-    private final DelayableExecutor mDelayableExecutor;
-    private final long mDoubleTapTimeMs;
     private final boolean mTestHarness;
     private final MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
@@ -98,19 +94,7 @@
                 @Override
         public void onGestureComplete(long completionTimeMs) {
             if (mPriorResults != null) {
-                // Single taps that may become double taps don't get added right away.
-                if (mClassifyAsSingleTap) {
-                    Collection<FalsingClassifier.Result> singleTapResults = mPriorResults;
-                    mSingleTapHistoryCanceller = mDelayableExecutor.executeDelayed(
-                            () -> {
-                                mSingleTapHistoryCanceller = null;
-                                mHistoryTracker.addResults(singleTapResults, completionTimeMs);
-                            },
-                            mDoubleTapTimeMs);
-                    mClassifyAsSingleTap = false;  // Don't treat things as single taps by default.
-                } else {
-                    mHistoryTracker.addResults(mPriorResults, completionTimeMs);
-                }
+                mHistoryTracker.addResults(mPriorResults, completionTimeMs);
                 mPriorResults = null;
             } else {
                 // Gestures that were not classified get treated as a false.
@@ -123,17 +107,13 @@
     };
 
     private Collection<FalsingClassifier.Result> mPriorResults;
-    private boolean mClassifyAsSingleTap;
-    private Runnable mSingleTapHistoryCanceller;
 
     @Inject
     public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
             DockManager dockManager, MetricsLogger metricsLogger,
             @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
             SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier,
-            HistoryTracker historyTracker, @Main DelayableExecutor delayableExecutor,
-            @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs,
-            @TestHarness boolean testHarness) {
+            HistoryTracker historyTracker, @TestHarness boolean testHarness) {
         mDataProvider = falsingDataProvider;
         mDockManager = dockManager;
         mMetricsLogger = metricsLogger;
@@ -141,8 +121,6 @@
         mSingleTapClassifier = singleTapClassifier;
         mDoubleTapClassifier = doubleTapClassifier;
         mHistoryTracker = historyTracker;
-        mDelayableExecutor = delayableExecutor;
-        mDoubleTapTimeMs = doubleTapTimeMs;
         mTestHarness = testHarness;
 
         mDataProvider.addSessionListener(mSessionListener);
@@ -158,7 +136,6 @@
     public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         boolean result;
 
-        mClassifyAsSingleTap = false;
         mDataProvider.setInteractionType(interactionType);
 
         if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
@@ -166,7 +143,7 @@
                     mClassifiers.stream().map(falsingClassifier -> {
                         FalsingClassifier.Result classifierResult =
                                 falsingClassifier.classifyGesture(
-                                        mHistoryTracker.falsePenalty(),
+                                        mHistoryTracker.falseBelief(),
                                         mHistoryTracker.falseConfidence());
                         if (classifierResult.isFalse()) {
                             logInfo(String.format(
@@ -217,9 +194,7 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck) {
-        mClassifyAsSingleTap = true;
-
+    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
         FalsingClassifier.Result singleTapResult =
                 mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
         mPriorResults = Collections.singleton(singleTapResult);
@@ -233,14 +208,24 @@
             return true;
         }
 
-        // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed
         if (robustCheck) {
-            boolean result = !mDataProvider.isJustUnlockedWithFace();
-            mPriorResults = Collections.singleton(
-                    result ? FalsingClassifier.Result.falsed(0.1, "no face detected")
-                            : FalsingClassifier.Result.passed(1));
-
-            return result;
+            if (mDataProvider.isJustUnlockedWithFace()) {
+                // Immediately pass if a face is detected.
+                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
+                return false;
+            } else if (!isFalseDoubleTap()) {
+                // We must check double tapping before other heuristics. This is because
+                // the double tap will fail if there's only been one tap. We don't want that
+                // failure to be recorded in mPriorResults.
+                return false;
+            } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) {
+                mPriorResults = Collections.singleton(
+                        FalsingClassifier.Result.falsed(0, "bad history"));
+                return true;
+            } else {
+                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
+                return false;
+            }
         }
 
         return false;
@@ -248,7 +233,6 @@
 
     @Override
     public boolean isFalseDoubleTap() {
-        mClassifyAsSingleTap = false;
         FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
         mPriorResults = Collections.singleton(result);
         if (result.isFalse()) {
@@ -258,12 +242,6 @@
             if (reason != null) {
                 logInfo(reason);
             }
-        } else {
-            // A valid double tap prevents an invalid single tap from going into history.
-            if (mSingleTapHistoryCanceller != null) {
-                mSingleTapHistoryCanceller.run();
-                mSingleTapHistoryCanceller = null;
-            }
         }
         return result.isFalse();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index bbb9371..ffcdb93 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -62,7 +62,7 @@
                 VERTICAL_ANGLE_RANGE);
     }
 
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         float angle = getAngle();
 
         if (angle == Float.MAX_VALUE) {  // Unknown angle
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 4cb5aa2..0f121c1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -147,7 +147,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         return !getPassedFlingThreshold()
                 ? Result.falsed(0.5, getReason()) : Result.passed(0.5);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
index f7fe14a..baa54a6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
@@ -46,14 +46,14 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         List<MotionEvent> secondTapEvents = getRecentMotionEvents();
         List<MotionEvent> firstTapEvents = getPriorMotionEvents();
 
         StringBuilder reason = new StringBuilder();
 
         if (firstTapEvents == null) {
-            return Result.falsed(1, "Only one gesture recorded");
+            return Result.falsed(0, "Only one gesture recorded");
         }
 
         return !isDoubleTap(firstTapEvents, secondTapEvents, reason)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
index f40045b..1af5f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
@@ -124,7 +124,7 @@
      * See also {@link #classifyGesture(double, double)}.
      */
     Result classifyGesture() {
-        return calculateFalsingResult(0, 0);
+        return calculateFalsingResult(0.5, 0);
     }
 
     /**
@@ -135,16 +135,16 @@
      *
      * See also {@link #classifyGesture()}.
      */
-    Result classifyGesture(double historyPenalty, double historyConfidence) {
-        return calculateFalsingResult(historyPenalty, historyConfidence);
+    Result classifyGesture(double historyBelief, double historyConfidence) {
+        return calculateFalsingResult(historyBelief, historyConfidence);
     }
 
     /**
      * Calculate a result based on available data.
      *
-     * When passed a historyConfidence of 0, the history penalty should be wholly ignored.
+     * When passed a historyConfidence of 0, the history belief should be wholly ignored.
      */
-    abstract Result calculateFalsingResult(double historyPenalty, double historyConfidence);
+    abstract Result calculateFalsingResult(double historyBelief, double historyConfidence);
 
     /** */
     public static void logDebug(String msg) {
@@ -164,7 +164,7 @@
     /**
      * A Falsing result that encapsulates the boolean result along with confidence and a reason.
      */
-    static class Result {
+    public static class Result {
         private final boolean mFalsed;
         private final double mConfidence;
         private final String mReason;
@@ -193,14 +193,14 @@
         /**
          * Construct a "falsed" result indicating that a gesture should be treated as accidental.
          */
-        static Result falsed(double confidence, String reason) {
+        public static Result falsed(double confidence, String reason) {
             return new Result(true, confidence, reason);
         }
 
         /**
          * Construct a "passed" result indicating that a gesture should be allowed.
          */
-        static Result passed(double confidence) {
+        public static Result passed(double confidence) {
             return new Result(false, confidence, null);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index b0bbab3..bb03720 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -120,5 +120,8 @@
 
     /** */
     void cleanup();
+
+    /** */
+    void updateFalseConfidence(FalsingClassifier.Result result);
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index 12a0604..939b45a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -153,4 +153,8 @@
     @Override
     public void cleanup() {
     }
+
+    @Override
+    public void updateFalseConfidence(FalsingClassifier.Result result) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index e08b43b..e090006 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -29,6 +29,9 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.SystemClock;
+
+import java.util.Collections;
 
 import javax.inject.Inject;
 
@@ -42,8 +45,10 @@
     private final FalsingDataProvider mFalsingDataProvider;
     private final FalsingManager mFalsingManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final HistoryTracker mHistoryTracker;
     private final ProximitySensor mProximitySensor;
     private final StatusBarStateController mStatusBarStateController;
+    private final SystemClock mSystemClock;
 
     private int mState;
     private boolean mShowingAod;
@@ -80,13 +85,16 @@
 
     @Inject
     FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            ProximitySensor proximitySensor, StatusBarStateController statusBarStateController) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor, HistoryTracker historyTracker,
+            ProximitySensor proximitySensor, StatusBarStateController statusBarStateController,
+            SystemClock systemClock) {
         mFalsingDataProvider = falsingDataProvider;
         mFalsingManager = falsingManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mHistoryTracker = historyTracker;
         mProximitySensor = proximitySensor;
         mStatusBarStateController = statusBarStateController;
+        mSystemClock = systemClock;
 
 
         mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
@@ -282,6 +290,11 @@
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
     }
 
+    @Override
+    public void updateFalseConfidence(FalsingClassifier.Result result) {
+        mHistoryTracker.addResults(Collections.singleton(result), mSystemClock.uptimeMillis());
+    }
+
     private void updateInteractionType(@Classifier.InteractionType int type) {
         logDebug("InteractionType: " + type);
         mFalsingDataProvider.setInteractionType(type);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index d4d8d06..aac27cb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -76,7 +76,7 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck) {
+    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
         return robustCheck ? mIsFalseRobustTap : mIsFalseTap;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index cbec057..e9bb48c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -131,8 +131,8 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck) {
-        return mInternalFalsingManager.isFalseTap(robustCheck);
+    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+        return mInternalFalsingManager.isFalseTap(robustCheck, falsePenalty);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
index 8bd94a1..be48ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
@@ -36,12 +36,17 @@
  */
 @SysUISingleton
 public class HistoryTracker {
-    private static final double HISTORY_DECAY = 0.8f;
+    private static final long HISTORY_MAX_AGE_MS = 10000;
+    // A score is decayed discretely every DECAY_INTERVAL_MS.
     private static final long DECAY_INTERVAL_MS = 100;
-    // We expire items once their decay factor is below 0.001.
-    private static final double MINIMUM_SCORE = 0.001;
-    private static final long TOTAL_DECAY_TIME_MS =
-            DECAY_INTERVAL_MS * (long) (Math.log(MINIMUM_SCORE) / Math.log(HISTORY_DECAY));
+    // We expire items once their decay factor is below 0.1.
+    private static final double MINIMUM_SCORE = 0.1;
+    // This magic number is the factor a score is reduced by every DECAY_INTERVAL_MS.
+    // Once a score is HISTORY_MAX_AGE_MS ms old, it will be reduced by being multiplied by
+    // MINIMUM_SCORE. The math below ensures that.
+    private static final double HISTORY_DECAY =
+            Math.pow(10, Math.log10(MINIMUM_SCORE) / HISTORY_MAX_AGE_MS * DECAY_INTERVAL_MS);
+
     private final SystemClock mSystemClock;
 
     DelayQueue<CombinedResult> mResults = new DelayQueue<>();
@@ -54,29 +59,36 @@
     /**
      * Returns how much the HistoryClassifier thinks the past events indicate pocket dialing.
      *
-     * A result of 0 means that all prior gestures succeeded or there is no data to
-     * calculate a score with. Use {@link #falseConfidence()} to differentiate between the
-     * two cases.
+     * A result close to 0.5 means that prior data is inconclusive (inconsistent, lacking
+     * confidence, or simply lacking in quantity).
      *
-     * A result of 1 means that all prior gestures were very obviously false. The current gesture
-     * might be valid, but it should have a high-bar to be classified as such.
+     * A result close to 0 means that prior gestures indicate a success.
+     *
+     * A result close to 1 means that prior gestures were very obviously false.
+     *
+     * The current gesture might be different than what is reported by this method, but there should
+     * be a high-bar to be classified differently.
      *
      * See also {@link #falseConfidence()}.
      */
-    double falsePenalty() {
+    double falseBelief() {
         //noinspection StatementWithEmptyBody
         while (mResults.poll() != null) {
             // Empty out the expired results.
         }
 
         if (mResults.isEmpty()) {
-            return 0;
+            return 0.5;
         }
 
         long nowMs = mSystemClock.uptimeMillis();
+        // Get our Bayes on.
         return mResults.stream()
                 .map(result -> result.getDecayedScore(nowMs))
-                .reduce(0.0, Double::sum) / mResults.size();
+                .reduce(0.5,
+                        (prior, measurement) ->
+                                (prior * measurement)
+                                        / (prior * measurement + (1 - prior) * (1 - measurement)));
     }
 
     /**
@@ -91,7 +103,7 @@
      * A result of 1 means that there are ample, fresh data to act upon that is all consistent
      * with each other.
      *
-     * See als {@link #falsePenalty()}.
+     * See als {@link #falseBelief()}.
      */
     double falseConfidence() {
         //noinspection StatementWithEmptyBody
@@ -126,6 +138,15 @@
 
         finalScore /= results.size();
 
+        // Never add a 0 or  1, else Bayes breaks down (a 0 and a 1 together results in NaN). In
+        // other words,  you shouldn't need Bayes if you have 100% confidence one way or another.
+        // Instead, make the number ever so slightly smaller so that our math never breaks.
+        if (finalScore == 1) {
+            finalScore = 0.99999;
+        } else if (finalScore == 0) {
+            finalScore = 0.00001;
+        }
+
         //noinspection StatementWithEmptyBody
         while (mResults.poll() != null) {
             // Empty out the expired results.
@@ -147,15 +168,17 @@
         private final double mScore;
 
         CombinedResult(long uptimeMillis, double score) {
-            mExpiryMs = uptimeMillis + TOTAL_DECAY_TIME_MS;
+            mExpiryMs = uptimeMillis + HISTORY_MAX_AGE_MS;
             mScore = score;
         }
 
         double getDecayedScore(long nowMs) {
             long remainingTimeMs = mExpiryMs - nowMs;
-            long decayedTimeMs = TOTAL_DECAY_TIME_MS - remainingTimeMs;
+            long decayedTimeMs = HISTORY_MAX_AGE_MS - remainingTimeMs;
             double timeIntervals = (double) decayedTimeMs / DECAY_INTERVAL_MS;
-            return mScore * Math.pow(HISTORY_DECAY, timeIntervals);
+
+            // Score should decay towards 0.5.
+            return (mScore - 0.5) * Math.pow(HISTORY_DECAY, timeIntervals) + 0.5;
         }
 
         double getScore() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
index cd399fe..77d2d42 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         int interactionType = getInteractionType();
         int allowedPointerCount =
                 (interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 9ee8598..6e97857 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -112,7 +112,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         if (getInteractionType() == QUICK_SETTINGS) {
             return Result.passed(0);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
index f2622ec..4dd20cc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
@@ -39,12 +39,15 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         return isTap(getRecentMotionEvents());
     }
 
     /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
     public Result isTap(List<MotionEvent> motionEvents) {
+        if (motionEvents.isEmpty()) {
+            return Result.falsed(0, "no motion events");
+        }
         float downX = motionEvents.get(0).getX();
         float downY = motionEvents.get(0).getY();
 
@@ -59,7 +62,7 @@
             } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
                 reason = "dY too big for a tap: "
                         + Math.abs(event.getY() - downY)
-                        + "vs "
+                        + " vs "
                         + mTouchSlop;
                 return Result.falsed(0.5, reason);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index d470d62..4e032ea 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -38,7 +38,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         boolean vertical = isVertical();
         boolean up = isUp();
         boolean right = isRight();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 2bfb218..2058257 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -84,7 +84,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         List<MotionEvent> motionEvents = getRecentMotionEvents();
         // Rotate horizontal gestures to be horizontal between their first and last point.
         // Rotate vertical gestures to be vertical between their first and last point.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
new file mode 100644
index 0000000..3bfdcae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls
+
+import android.service.controls.DeviceTypes.DeviceType
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.controls.ui.ControlViewHolder
+
+/**
+ * Interface for logging UI events related to controls
+ */
+interface ControlsMetricsLogger {
+
+    /**
+     * Assign a new instance id for this controls session, defined as when the controls area is
+     * made visible to when it is closed.
+     */
+    fun assignInstanceId()
+
+    fun touch(cvh: ControlViewHolder, isLocked: Boolean) {
+        log(ControlsEvents.CONTROL_TOUCH.id, cvh.deviceType, cvh.uid, isLocked)
+    }
+
+    fun drag(cvh: ControlViewHolder, isLocked: Boolean) {
+        log(ControlsEvents.CONTROL_DRAG.id, cvh.deviceType, cvh.uid, isLocked)
+    }
+
+    fun longPress(cvh: ControlViewHolder, isLocked: Boolean) {
+        log(ControlsEvents.CONTROL_LONG_PRESS.id, cvh.deviceType, cvh.uid, isLocked)
+    }
+
+    fun refreshBegin(uid: Int, isLocked: Boolean) {
+        assignInstanceId()
+        log(ControlsEvents.CONTROL_REFRESH_BEGIN.id, 0, uid, isLocked)
+    }
+
+    fun refreshEnd(cvh: ControlViewHolder, isLocked: Boolean) {
+        log(ControlsEvents.CONTROL_REFRESH_END.id, cvh.deviceType, cvh.uid, isLocked)
+    }
+
+    /**
+     * Logs a controls-related event
+     *
+     * @param eventId Main UIEvent to capture
+     * @param deviceType One of {@link android.service.controls.DeviceTypes}
+     * @param packageName Package name of the service that receives the request
+     * @param isLocked Is the device locked at the start of the action?
+     */
+    fun log(
+        eventId: Int,
+        @DeviceType deviceType: Int,
+        uid: Int,
+        isLocked: Boolean
+    )
+
+    private enum class ControlsEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "User touched a control")
+        CONTROL_TOUCH(714),
+
+        @UiEvent(doc = "User dragged a control")
+        CONTROL_DRAG(713),
+
+        @UiEvent(doc = "User long-pressed a control")
+        CONTROL_LONG_PRESS(715),
+
+        @UiEvent(doc = "User has opened controls, and a state refresh has begun")
+        CONTROL_REFRESH_BEGIN(716),
+
+        @UiEvent(doc = "User has opened controls, and a state refresh has ended")
+        CONTROL_REFRESH_END(717);
+
+        override fun getId() = metricId
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
new file mode 100644
index 0000000..c165632
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
@@ -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 com.android.systemui.controls
+
+import android.service.controls.DeviceTypes.DeviceType
+
+import com.android.internal.logging.InstanceIdSequence
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+
+import javax.inject.Inject
+
+/**
+ * Implementation for logging UI events related to controls
+ */
+@SysUISingleton
+class ControlsMetricsLoggerImpl @Inject constructor() : ControlsMetricsLogger {
+
+    companion object {
+        private const val INSTANCE_ID_MAX = 1 shl 13
+    }
+
+    private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
+    private var instanceId = 0
+
+    override fun assignInstanceId() {
+        instanceId = instanceIdSequence.newInstanceId().id
+    }
+
+    /**
+     * {@see ControlsMetricsLogger#log}
+     */
+    override fun log(
+        eventId: Int,
+        @DeviceType deviceType: Int,
+        uid: Int,
+        isLocked: Boolean
+    ) {
+        SysUiStatsLog.write(
+            SysUiStatsLog.DEVICE_CONTROL_CHANGED,
+            eventId,
+            instanceId,
+            deviceType,
+            uid,
+            isLocked
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index fbdeb30..a165bb2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -18,6 +18,8 @@
 
 import android.app.Activity
 import android.content.pm.PackageManager
+import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.ControlsMetricsLoggerImpl
 import com.android.systemui.controls.controller.ControlsBindingController
 import com.android.systemui.controls.controller.ControlsBindingControllerImpl
 import com.android.systemui.controls.controller.ControlsController
@@ -31,6 +33,7 @@
 import com.android.systemui.controls.management.ControlsRequestDialog
 import com.android.systemui.controls.ui.ControlActionCoordinator
 import com.android.systemui.controls.ui.ControlActionCoordinatorImpl
+import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.controls.ui.ControlsUiControllerImpl
 import com.android.systemui.dagger.SysUISingleton
@@ -79,6 +82,9 @@
     abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
 
     @Binds
+    abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger
+
+    @Binds
     abstract fun provideControlActionCoordinator(
         coordinator: ControlActionCoordinatorImpl
     ): ControlActionCoordinator
@@ -113,4 +119,9 @@
     abstract fun provideControlsRequestDialog(
         activity: ControlsRequestDialog
     ): Activity
+
+    @Binds
+    @IntoMap
+    @ClassKey(ControlsActivity::class)
+    abstract fun provideControlsActivity(activity: ControlsActivity): Activity
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index fc89783..7dd1d28 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.settings.CurrentUserTracker
@@ -112,7 +112,11 @@
         if (backToGlobalActions) {
             globalActionsComponent.handleShowGlobalActionsMenu()
         } else {
-            ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+            val i = Intent().apply {
+                component = ComponentName(applicationContext, ControlsActivity::class.java)
+                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+            }
+            startActivity(i)
         }
         animateExitAndFinish()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 2d647a9..3099014 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -40,7 +40,7 @@
 import com.android.systemui.controls.TooltipManager
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.globalactions.GlobalActionsComponent
@@ -352,7 +352,11 @@
         if (backToGlobalActions) {
             globalActionsComponent.handleShowGlobalActionsMenu()
         } else {
-            ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+            val i = Intent().apply {
+                component = ComponentName(applicationContext, ControlsActivity::class.java)
+                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+            }
+            startActivity(i)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index d5e41d0..fa1c41f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -116,7 +116,11 @@
         if (backToGlobalActions) {
             globalActionsComponent.handleShowGlobalActionsMenu()
         } else {
-            ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+            val i = Intent().apply {
+                component = ComponentName(applicationContext, ControlsActivity::class.java)
+                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+            }
+            startActivity(i)
         }
         animateExitAndFinish()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index d06568a..0db15e8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.controls.ui
 
+import android.content.Context
 import android.service.controls.Control
 
 /**
@@ -24,8 +25,8 @@
  */
 interface ControlActionCoordinator {
 
-    // Handle actions launched from GlobalActionsDialog or ControlDialog
-    var startedFromGlobalActions: Boolean
+    // If launched from an Activity, continue within this stack
+    var activityContext: Context?
 
     /**
      * Close any dialogs which may have been open
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 6b300f4..477c220 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -32,6 +32,7 @@
 import android.view.HapticFeedbackConstants
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.globalactions.GlobalActionsComponent
@@ -53,14 +54,16 @@
     private val globalActionsComponent: GlobalActionsComponent,
     private val taskViewFactory: Optional<TaskViewFactory>,
     private val broadcastDispatcher: BroadcastDispatcher,
-    private val lazyUiController: Lazy<ControlsUiController>
+    private val lazyUiController: Lazy<ControlsUiController>,
+    private val controlsMetricsLogger: ControlsMetricsLogger
 ) : ControlActionCoordinator {
     private var dialog: Dialog? = null
     private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
     private var pendingAction: Action? = null
     private var actionsInProgress = mutableSetOf<String>()
-
-    override var startedFromGlobalActions: Boolean = true
+    private val isLocked: Boolean
+        get() = !keyguardStateController.isUnlocked()
+    override var activityContext: Context? = null
 
     companion object {
         private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
@@ -72,6 +75,7 @@
     }
 
     override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
+        controlsMetricsLogger.touch(cvh, isLocked)
         bouncerOrRun(createAction(cvh.cws.ci.controlId, {
             cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
             cvh.action(BooleanAction(templateId, !isChecked))
@@ -79,11 +83,12 @@
     }
 
     override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
+        controlsMetricsLogger.touch(cvh, isLocked)
         val blockable = cvh.usePanel()
         bouncerOrRun(createAction(cvh.cws.ci.controlId, {
             cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
             if (cvh.usePanel()) {
-                showDialog(cvh, control.getAppIntent().getIntent())
+                showDetail(cvh, control.getAppIntent().getIntent())
             } else {
                 cvh.action(CommandAction(templateId))
             }
@@ -99,23 +104,25 @@
     }
 
     override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
+        controlsMetricsLogger.drag(cvh, isLocked)
         bouncerOrRun(createAction(cvh.cws.ci.controlId, {
             cvh.action(FloatAction(templateId, newValue))
         }, false /* blockable */))
     }
 
     override fun longPress(cvh: ControlViewHolder) {
+        controlsMetricsLogger.longPress(cvh, isLocked)
         bouncerOrRun(createAction(cvh.cws.ci.controlId, {
             // Long press snould only be called when there is valid control state, otherwise ignore
             cvh.cws.control?.let {
                 cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
-                showDialog(cvh, it.getAppIntent().getIntent())
+                showDetail(cvh, it.getAppIntent().getIntent())
             }
         }, false /* blockable */))
     }
 
     override fun runPendingAction(controlId: String) {
-        if (!keyguardStateController.isUnlocked()) return
+        if (isLocked) return
         if (pendingAction?.controlId == controlId) {
             pendingAction?.invoke()
             pendingAction = null
@@ -140,22 +147,17 @@
     @VisibleForTesting
     fun bouncerOrRun(action: Action) {
         if (keyguardStateController.isShowing()) {
-            var closeDialog = !keyguardStateController.isUnlocked()
-            if (closeDialog) {
+            if (isLocked) {
                 context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
 
                 // pending actions will only run after the control state has been refreshed
                 pendingAction = action
             }
-
+            val wasLocked = isLocked
             activityStarter.dismissKeyguardThenExecute({
                 Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
-                if (closeDialog) {
-                    if (startedFromGlobalActions) {
-                        globalActionsComponent.handleShowGlobalActionsMenu()
-                    } else {
-                        ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get())
-                    }
+                if (wasLocked && activityContext == null) {
+                    globalActionsComponent.handleShowGlobalActionsMenu()
                 } else {
                     action.invoke()
                 }
@@ -170,9 +172,9 @@
         bgExecutor.execute { vibrator.vibrate(effect) }
     }
 
-    private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
+    private fun showDetail(cvh: ControlViewHolder, intent: Intent) {
         bgExecutor.execute {
-            val activities: List<ResolveInfo> = cvh.context.packageManager.queryIntentActivities(
+            val activities: List<ResolveInfo> = context.packageManager.queryIntentActivities(
                 intent,
                 PackageManager.MATCH_DEFAULT_ONLY
             )
@@ -180,8 +182,8 @@
             uiExecutor.execute {
                 // make sure the intent is valid before attempting to open the dialog
                 if (activities.isNotEmpty() && taskViewFactory.isPresent) {
-                    taskViewFactory.get().create(cvh.context, uiExecutor, {
-                        dialog = DetailDialog(cvh, it, intent).also {
+                    taskViewFactory.get().create(context, uiExecutor, {
+                        dialog = DetailDialog(activityContext, it, intent, cvh).also {
                             it.setOnDismissListener { _ -> dialog = null }
                             it.show()
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 9d92a40..3e02890 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -49,6 +49,7 @@
 import com.android.internal.graphics.ColorUtils
 import com.android.systemui.Interpolators
 import com.android.systemui.R
+import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import kotlin.reflect.KClass
@@ -63,7 +64,9 @@
     val controlsController: ControlsController,
     val uiExecutor: DelayableExecutor,
     val bgExecutor: DelayableExecutor,
-    val controlActionCoordinator: ControlActionCoordinator
+    val controlActionCoordinator: ControlActionCoordinator,
+    val controlsMetricsLogger: ControlsMetricsLogger,
+    val uid: Int
 ) {
 
     companion object {
@@ -141,7 +144,7 @@
         status.setSelected(true)
     }
 
-    fun bindData(cws: ControlWithState) {
+    fun bindData(cws: ControlWithState, isLocked: Boolean) {
         // If an interaction is in progress, the update may visually interfere with the action the
         // action the user wants to make. Don't apply the update, and instead assume a new update
         // will coming from when the user interaction is complete.
@@ -171,10 +174,16 @@
             controlActionCoordinator.runPendingAction(cws.ci.controlId)
         }
 
+        val wasLoading = isLoading
         isLoading = false
         behavior = bindBehavior(behavior,
             findBehaviorClass(controlStatus, controlTemplate, deviceType))
         updateContentDescription()
+
+        // Only log one event per control, at the moment we have determined that the control
+        // switched from the loading to done state
+        val doneLoading = wasLoading && !isLoading
+        if (doneLoading) controlsMetricsLogger.refreshEnd(this, isLocked)
     }
 
     fun actionResponse(@ControlAction.ResponseResult response: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
new file mode 100644
index 0000000..a35b792
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowInsets.Type
+
+import com.android.systemui.R
+import com.android.systemui.controls.management.ControlsAnimations
+import com.android.systemui.util.LifecycleActivity
+import javax.inject.Inject
+
+/**
+ * Displays Device Controls inside an activity
+ */
+class ControlsActivity @Inject constructor(
+    private val uiController: ControlsUiController
+) : LifecycleActivity() {
+
+    private lateinit var parent: ViewGroup
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(R.layout.controls_fullscreen)
+
+        requireViewById<ViewGroup>(R.id.control_detail_root).apply {
+            setOnApplyWindowInsetsListener {
+                v: View, insets: WindowInsets ->
+                    v.apply {
+                        val l = getPaddingLeft()
+                        val t = getPaddingTop()
+                        val r = getPaddingRight()
+                        setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
+                    }
+
+                WindowInsets.CONSUMED
+            }
+        }
+    }
+
+    override fun onStart() {
+        super.onStart()
+
+        parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
+        parent.alpha = 0f
+        uiController.show(parent, { animateExitAndFinish() }, this)
+    }
+
+    override fun onResume() {
+        super.onResume()
+
+        ControlsAnimations.enterAnimation(parent).start()
+    }
+
+    override fun onBackPressed() {
+        animateExitAndFinish()
+    }
+
+    override fun onStop() {
+        super.onStop()
+
+        uiController.hide()
+    }
+
+    private fun animateExitAndFinish() {
+        ControlsAnimations.exitAnimation(parent, { finish() }).start()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
deleted file mode 100644
index 537334a..0000000
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.controls.ui
-
-import android.app.Dialog
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowManager
-
-import com.android.systemui.Interpolators
-import com.android.systemui.R
-import com.android.systemui.broadcast.BroadcastDispatcher
-import javax.inject.Inject
-
-/**
- * Show the controls space inside a dialog, as from the lock screen.
- */
-class ControlsDialog @Inject constructor(
-    thisContext: Context,
-    val broadcastDispatcher: BroadcastDispatcher
-) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
-
-    private val receiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            val action = intent.getAction()
-            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                dismiss()
-            }
-        }
-    }
-
-    init {
-        window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
-        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-                or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
-
-        setContentView(R.layout.controls_in_dialog)
-
-        requireViewById<ViewGroup>(R.id.control_detail_root).apply {
-            setOnClickListener { dismiss() }
-            (getParent() as View).setOnClickListener { dismiss() }
-        }
-    }
-
-    fun show(
-        controller: ControlsUiController
-    ): ControlsDialog {
-        super.show()
-
-        val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
-        vg.alpha = 0f
-        controller.show(vg, { dismiss() }, false /* startedFromGlobalActions */)
-
-        vg.animate()
-            .alpha(1f)
-            .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
-            .setDuration(300)
-
-        val filter = IntentFilter()
-        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
-        broadcastDispatcher.registerReceiver(receiver, filter)
-
-        return this
-    }
-
-    override fun dismiss() {
-        broadcastDispatcher.unregisterReceiver(receiver)
-
-        if (!isShowing()) return
-
-        super.dismiss()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 20bdf60..f86948e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.controls.ui
 
 import android.content.ComponentName
+import android.content.Context
 import android.service.controls.Control
 import android.service.controls.actions.ControlAction
 import android.view.ViewGroup
@@ -30,7 +31,7 @@
         public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
     }
 
-    fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
+    fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context?)
     fun hide()
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index c94d85a..d08882b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -43,6 +43,7 @@
 import android.widget.Space
 import android.widget.TextView
 import com.android.systemui.R
+import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlInfo
@@ -58,6 +59,7 @@
 import com.android.systemui.globalactions.GlobalActionsPopupMenu
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import dagger.Lazy
 import java.text.Collator
@@ -77,7 +79,9 @@
     val controlActionCoordinator: ControlActionCoordinator,
     private val activityStarter: ActivityStarter,
     private val shadeController: ShadeController,
-    private val iconCache: CustomIconCache
+    private val iconCache: CustomIconCache,
+    private val controlsMetricsLogger: ControlsMetricsLogger,
+    private val keyguardStateController: KeyguardStateController
 ) : ControlsUiController {
 
     companion object {
@@ -133,7 +137,8 @@
         return object : ControlsListingController.ControlsListingCallback {
             override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
                 val lastItems = serviceInfos.map {
-                    SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
+                    val uid = it.serviceInfo.applicationInfo.uid
+                    SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName, uid)
                 }
                 uiExecutor.execute {
                     parent.removeAllViews()
@@ -148,7 +153,7 @@
     override fun show(
         parent: ViewGroup,
         onDismiss: Runnable,
-        startedFromGlobalActions: Boolean
+        activityContext: Context?
     ) {
         Log.d(ControlsUiController.TAG, "show()")
         this.parent = parent
@@ -156,7 +161,7 @@
         hidden = false
         retainCache = false
 
-        controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions
+        controlActionCoordinator.activityContext = activityContext
 
         allStructures = controlsController.get().getFavorites()
         selectedStructure = loadPreference(allStructures)
@@ -193,7 +198,7 @@
                 controlViewsById.clear()
                 controlsById.clear()
 
-                show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions)
+                show(parent, onDismiss, controlActionCoordinator.activityContext)
                 val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
                 showAnim.setInterpolator(DecelerateInterpolator(1.0f))
                 showAnim.setDuration(FADE_IN_MILLIS)
@@ -268,7 +273,7 @@
         intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
         intent.putExtra(
             ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-            controlActionCoordinator.startedFromGlobalActions
+            controlActionCoordinator.activityContext == null
         )
         onDismiss.run()
 
@@ -282,8 +287,19 @@
     private fun showControlsView(items: List<SelectionItem>) {
         controlViewsById.clear()
 
-        createListView()
-        createDropDown(items)
+        val itemsByComponent = items.associateBy { it.componentName }
+        val itemsWithStructure = mutableListOf<SelectionItem>()
+        allStructures.mapNotNullTo(itemsWithStructure) {
+            itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
+        }
+        itemsWithStructure.sortWith(localeComparator)
+
+        val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
+
+        controlsMetricsLogger.refreshBegin(selectionItem.uid, !keyguardStateController.isUnlocked())
+
+        createListView(selectionItem)
+        createDropDown(itemsWithStructure, selectionItem)
         createMenu()
     }
 
@@ -325,22 +341,13 @@
         })
     }
 
-    private fun createDropDown(items: List<SelectionItem>) {
+    private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) {
         items.forEach {
             RenderInfo.registerComponentIcon(it.componentName, it.icon)
         }
 
-        val itemsByComponent = items.associateBy { it.componentName }
-        val itemsWithStructure = mutableListOf<SelectionItem>()
-        allStructures.mapNotNullTo(itemsWithStructure) {
-            itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
-        }
-        itemsWithStructure.sortWith(localeComparator)
-
-        val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
-
         var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply {
-            addAll(itemsWithStructure)
+            addAll(items)
         }
 
         /*
@@ -349,13 +356,13 @@
          * a similar effect
          */
         val spinner = parent.requireViewById<TextView>(R.id.app_or_structure_spinner).apply {
-            setText(selectionItem.getTitle())
+            setText(selected.getTitle())
             // override the default color on the dropdown drawable
             (getBackground() as LayerDrawable).getDrawable(0)
                 .setTint(context.resources.getColor(R.color.control_spinner_dropdown, null))
         }
 
-        if (itemsWithStructure.size == 1) {
+        if (items.size == 1) {
             spinner.setBackground(null)
             return
         }
@@ -388,10 +395,21 @@
         })
     }
 
-    private fun createListView() {
+    private fun createListView(selected: SelectionItem) {
         val inflater = LayoutInflater.from(context)
         inflater.inflate(R.layout.controls_with_favorites, parent, true)
 
+        if (controlActionCoordinator.activityContext == null) {
+            parent.requireViewById<View>(R.id.controls_spacer).apply {
+                visibility = View.VISIBLE
+            }
+        } else {
+            parent.requireViewById<ImageView>(R.id.controls_close).apply {
+                setOnClickListener { _: View -> onDismiss.run() }
+                visibility = View.VISIBLE
+            }
+        }
+
         val maxColumns = findMaxColumns()
 
         val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
@@ -410,9 +428,11 @@
                     controlsController.get(),
                     uiExecutor,
                     bgExecutor,
-                    controlActionCoordinator
+                    controlActionCoordinator,
+                    controlsMetricsLogger,
+                    selected.uid
                 )
-                cvh.bindData(it)
+                cvh.bindData(it, false /* isLocked, will be ignored on initial load */)
                 controlViewsById.put(key, cvh)
             }
         }
@@ -502,6 +522,8 @@
     override fun hide() {
         hidden = true
 
+        controlActionCoordinator.activityContext = null
+
         closeDialogs(true)
         controlsController.get().unsubscribe()
 
@@ -515,6 +537,7 @@
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
+        val isLocked = !keyguardStateController.isUnlocked()
         controls.forEach { c ->
             controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
                 Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
@@ -523,8 +546,8 @@
                 val key = ControlKey(componentName, c.getControlId())
                 controlsById.put(key, cws)
 
-                uiExecutor.execute {
-                    controlViewsById.get(key)?.bindData(cws)
+                controlViewsById.get(key)?.let {
+                    uiExecutor.execute { it.bindData(cws, isLocked) }
                 }
             }
         }
@@ -553,7 +576,8 @@
     val appName: CharSequence,
     val structure: CharSequence,
     val icon: Drawable,
-    val componentName: ComponentName
+    val componentName: ComponentName,
+    val uid: Int
 ) {
     fun getTitle() = if (structure.isEmpty()) { appName } else { structure }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 020694d..9c788df 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -22,8 +22,8 @@
 import android.app.Dialog
 import android.app.PendingIntent
 import android.content.ComponentName
+import android.content.Context
 import android.content.Intent
-import android.provider.Settings
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowInsets
@@ -35,18 +35,20 @@
 import com.android.wm.shell.TaskView
 
 /**
- * A dialog that provides an {@link ActivityView}, allowing the application to provide
+ * A dialog that provides an {@link TaskView}, allowing the application to provide
  * additional information and actions pertaining to a {@link android.service.controls.Control}.
  * The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
  */
 class DetailDialog(
-    val cvh: ControlViewHolder,
-    val activityView: TaskView,
-    val intent: Intent
-) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) {
-
+    val activityContext: Context?,
+    val taskView: TaskView,
+    val intent: Intent,
+    val cvh: ControlViewHolder
+) : Dialog(
+    activityContext ?: cvh.context,
+    R.style.Theme_SystemUI_Dialog_Control_DetailPanel
+) {
     companion object {
-        private const val PANEL_TOP_OFFSET = "systemui.controls_panel_top_offset"
         /*
          * Indicate to the activity that it is being rendered in a bottomsheet, and they
          * should optimize the layout for a smaller space.
@@ -71,10 +73,19 @@
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
 
-            activityView.startActivity(
-                    PendingIntent.getActivity(context, 0, launchIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
-                    null, ActivityOptions.makeBasic())
+            val options = activityContext?.let {
+                ActivityOptions.makeCustomAnimation(
+                    it,
+                    0 /* enterResId */,
+                    0 /* exitResId */
+                )
+            } ?: ActivityOptions.makeBasic()
+            taskView.startActivity(
+                PendingIntent.getActivity(context, 0, launchIntent,
+                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
+                null,
+                options
+            )
         }
 
         override fun onTaskRemovalStarted(taskId: Int) {
@@ -92,7 +103,10 @@
     }
 
     init {
-        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+        if (activityContext == null) {
+            window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+        }
+
         // To pass touches to the task inside TaskView.
         window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
         window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
@@ -100,7 +114,7 @@
         setContentView(R.layout.controls_detail_dialog)
 
         requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
-            addView(activityView)
+            addView(taskView)
         }
 
         requireViewById<ImageView>(R.id.control_detail_close).apply {
@@ -120,48 +134,34 @@
 
         // consume all insets to achieve slide under effect
         window.getDecorView().setOnApplyWindowInsetsListener {
-            _: View, insets: WindowInsets ->
-                activityView.apply {
+            v: View, insets: WindowInsets ->
+                taskView.apply {
                     val l = getPaddingLeft()
                     val t = getPaddingTop()
                     val r = getPaddingRight()
                     setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
                 }
 
+                val l = v.getPaddingLeft()
+                val b = v.getPaddingBottom()
+                val r = v.getPaddingRight()
+                v.setPadding(l, insets.getInsets(Type.systemBars()).top, r, b)
+
                 WindowInsets.CONSUMED
         }
 
-        requireViewById<ViewGroup>(R.id.control_detail_root).apply {
-            // use flag only temporarily for testing
-            val resolver = cvh.context.contentResolver
-            val defaultOffsetInPx = cvh.context.resources
-                .getDimensionPixelSize(R.dimen.controls_activity_view_top_offset)
-            val offsetInPx = Settings.Secure.getInt(resolver, PANEL_TOP_OFFSET, defaultOffsetInPx)
-
-            val lp = getLayoutParams() as ViewGroup.MarginLayoutParams
-            lp.topMargin = offsetInPx
-            setLayoutParams(lp)
-
-            setOnClickListener { dismiss() }
-            (getParent() as View).setOnClickListener { dismiss() }
-        }
-
         if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(context.getResources())) {
             val cornerRadius = context.resources
                 .getDimensionPixelSize(R.dimen.controls_activity_view_corner_radius)
-            activityView.setCornerRadius(cornerRadius.toFloat())
+            taskView.setCornerRadius(cornerRadius.toFloat())
         }
-    }
 
-    override fun show() {
-        activityView.setListener(cvh.uiExecutor, stateCallback)
-
-        super.show()
+        taskView.setListener(cvh.uiExecutor, stateCallback)
     }
 
     override fun dismiss() {
         if (!isShowing()) return
-        activityView.release()
+        taskView.release()
 
         super.dismiss()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index a2f96bb..d02ff91f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -349,13 +349,6 @@
     /** */
     @Provides
     @SysUISingleton
-    public SystemActions providesSystemActions(Context context) {
-        return new SystemActions(context);
-    }
-
-    /** */
-    @Provides
-    @SysUISingleton
     public Choreographer providesChoreographer() {
         return Choreographer.getInstance();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index c26cc44..d8ade2b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -225,8 +225,8 @@
      * Appends wake-display event to the logs.
      * @param wake if we're waking up or sleeping.
      */
-    public void traceWakeDisplay(boolean wake) {
-        mLogger.logWakeDisplay(wake);
+    public void traceWakeDisplay(boolean wake, @Reason int reason) {
+        mLogger.logWakeDisplay(wake, reason);
     }
 
     /**
@@ -380,6 +380,7 @@
             case REASON_SENSOR_WAKE_UP: return "wakeup";
             case REASON_SENSOR_TAP: return "tap";
             case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps";
+            case REASON_SENSOR_QUICK_PICKUP: return "quickPickup";
             default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
         }
     }
@@ -389,7 +390,7 @@
             PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
             PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
             PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP,
-            REASON_SENSOR_UDFPS_LONG_PRESS})
+            REASON_SENSOR_UDFPS_LONG_PRESS, REASON_SENSOR_QUICK_PICKUP})
     public @interface Reason {}
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -403,6 +404,7 @@
     public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
     public static final int REASON_SENSOR_TAP = 9;
     public static final int REASON_SENSOR_UDFPS_LONG_PRESS = 10;
+    public static final int REASON_SENSOR_QUICK_PICKUP = 11;
 
-    public static final int TOTAL_REASONS = 11;
+    public static final int TOTAL_REASONS = 12;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index fddefae..9bc74be 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.doze
 
+import android.view.Display
 import com.android.systemui.doze.DozeLog.Reason
 import com.android.systemui.doze.DozeLog.reasonToString
 import com.android.systemui.log.LogBuffer
@@ -161,17 +162,18 @@
 
     fun logDisplayStateChanged(displayState: Int) {
         buffer.log(TAG, INFO, {
-            int1 = displayState
+            str1 = Display.stateToString(displayState)
         }, {
-            "Display state changed to $int1"
+            "Display state changed to $str1"
         })
     }
 
-    fun logWakeDisplay(isAwake: Boolean) {
+    fun logWakeDisplay(isAwake: Boolean, @Reason reason: Int) {
         buffer.log(TAG, DEBUG, {
             bool1 = isAwake
+            int1 = reason
         }, {
-            "Display wakefulness changed, isAwake=$bool1"
+            "Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index c542e5b..52c9f16 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -113,6 +113,8 @@
         mCallback = callback;
         mProximitySensor = proximitySensor;
 
+        boolean udfpsEnrolled =
+                authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
         boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
         mSensors = new TriggerSensor[] {
                 new TriggerSensor(
@@ -159,7 +161,7 @@
                         findSensorWithType(config.udfpsLongPressSensorType()),
                         "doze_pulse_on_auth",
                         true /* settingDef */,
-                        authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser()),
+                        udfpsEnrolled,
                         DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
                         true /* reports touch coordinates */,
                         true /* touchscreen */,
@@ -181,6 +183,15 @@
                         false /* touchscreen */,
                         mConfig.getWakeLockScreenDebounce(),
                         dozeLog),
+                new TriggerSensor(
+                        findSensorWithType(config.quickPickupSensorType()),
+                        Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
+                        false /* setting default */,
+                        config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
+                                && udfpsEnrolled,
+                        DozeLog.REASON_SENSOR_QUICK_PICKUP,
+                        false /* touchCoords */,
+                        false /* touchscreen */, dozeLog),
         };
 
         setProxListening(false);  // Don't immediately start listening when we register.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index c617f3d..04b4670 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -42,10 +42,13 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.settings.SecureSettings;
@@ -76,6 +79,7 @@
      * Assuming that the screen should start on.
      */
     private static boolean sWakeDisplaySensorState = true;
+    private Runnable mQuickPickupDozeCancellable;
 
     private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
 
@@ -96,6 +100,8 @@
     private final ProximitySensor.ProximityCheck mProxCheck;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final AuthController mAuthController;
+    private final DelayableExecutor mMainExecutor;
+    private final DelayableExecutor mBgExecutor;
 
     private long mNotificationPulseTime;
     private boolean mPulsePending;
@@ -135,7 +141,10 @@
         DOZING_UPDATE_SENSOR_TAP(441),
 
         @UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.")
-        DOZING_UPDATE_AUTH_TRIGGERED(657);
+        DOZING_UPDATE_AUTH_TRIGGERED(657),
+
+        @UiEvent(doc = "Dozing updated because quick pickup sensor woke up.")
+        DOZING_UPDATE_QUICK_PICKUP(708);
 
         private final int mId;
 
@@ -160,6 +169,7 @@
                 case 8: return DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN;
                 case 9: return DOZING_UPDATE_SENSOR_TAP;
                 case 10: return DOZING_UPDATE_AUTH_TRIGGERED;
+                case 11: return DOZING_UPDATE_QUICK_PICKUP;
                 default: return null;
             }
         }
@@ -172,7 +182,8 @@
             WakeLock wakeLock, DockManager dockManager,
             ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
             DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
-            SecureSettings secureSettings, AuthController authController) {
+            SecureSettings secureSettings, AuthController authController,
+            @Main DelayableExecutor mainExecutor, @Background DelayableExecutor bgExecutor) {
         mContext = context;
         mDozeHost = dozeHost;
         mConfig = config;
@@ -189,6 +200,8 @@
         mDozeLog = dozeLog;
         mBroadcastDispatcher = broadcastDispatcher;
         mAuthController = authController;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
     }
 
     @Override
@@ -262,18 +275,22 @@
         boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
         boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
-        boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
-        boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+        boolean isWakeOnPresence = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+        boolean isWakeOnReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
         boolean isUdfpsLongPress = pulseReason == DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
-        boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
+        boolean isQuickPickup = pulseReason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
+        boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach)
+                && rawValues != null && rawValues.length > 0 && rawValues[0] != 0);
 
-        if (isWakeDisplay) {
-            onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
+        if (isWakeOnPresence || isQuickPickup) {
+            onWakeScreen(isQuickPickup || isWakeDisplayEvent,
+                    mMachine.isExecutingTransition() ? null : mMachine.getState(),
+                    pulseReason);
         } else if (isLongPress) {
             requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
                     null /* onPulseSuppressedListener */);
-        } else if (isWakeLockScreen) {
-            if (wakeEvent) {
+        } else if (isWakeOnReach) {
+            if (isWakeDisplayEvent) {
                 requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
                         null /* onPulseSuppressedListener */);
             }
@@ -370,13 +387,17 @@
      * @param state The current state, or null if the state could not be determined due to enqueued
      *              transitions.
      */
-    private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) {
-        mDozeLog.traceWakeDisplay(wake);
-        sWakeDisplaySensorState = wake;
+    private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) {
+        mDozeLog.traceWakeDisplay(wake, reason);
+        final boolean isWakeOnPresence = reason == DozeLog.REASON_SENSOR_WAKE_UP;
+        final boolean isQuickPickup = reason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
+        if (isWakeOnPresence) {
+            sWakeDisplaySensorState = wake;
+        }
 
         if (wake) {
             proximityCheckThenCall((result) -> {
-                if (result !=  null && result) {
+                if (result != null && result) {
                     // In pocket, drop event.
                     return;
                 }
@@ -385,26 +406,51 @@
                     // Logs AOD open due to sensor wake up.
                     mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
                             .setType(MetricsEvent.TYPE_OPEN)
-                            .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+                            .setSubtype(reason));
+
+                    if (isQuickPickup) {
+                        // schedule runnable to go back to DOZE
+                        onQuickPickup();
+                    }
+                } else if (state == DozeMachine.State.DOZE_AOD && isQuickPickup) {
+                    // elongate time in DOZE_AOD, schedule new runnable to go back to DOZE
+                    onQuickPickup();
                 }
-            }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+            }, isQuickPickup /* alreadyPerformedProxCheck */, reason);
         } else {
             boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
             boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+            boolean pulse = (state == DozeMachine.State.DOZE_REQUEST_PULSE)
+                    || (state == DozeMachine.State.DOZE_PULSING)
+                    || (state == DozeMachine.State.DOZE_PULSING_BRIGHT);
+            boolean docked = (state == DozeMachine.State.DOZE_AOD_DOCKED);
             if (!pausing && !paused) {
+                if (isQuickPickup && (pulse || docked)) {
+                    return;
+                }
                 mMachine.requestState(DozeMachine.State.DOZE);
                 // Logs AOD close due to sensor wake up.
                 mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
                         .setType(MetricsEvent.TYPE_CLOSE)
-                        .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+                        .setSubtype(reason));
             }
         }
     }
 
+    private void onQuickPickup() {
+        cancelQuickPickupDelayableDoze();
+        mQuickPickupDozeCancellable = mMainExecutor.executeDelayed(() -> {
+            onWakeScreen(false,
+                    mMachine.isExecutingTransition() ? null : mMachine.getState(),
+                    DozeLog.REASON_SENSOR_QUICK_PICKUP);
+        }, mDozeParameters.getQuickPickupAodDuration());
+    }
+
     @Override
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         switch (newState) {
             case INITIALIZED:
+                sWakeDisplaySensorState = true;
                 mBroadcastReceiver.register(mBroadcastDispatcher);
                 mDozeHost.addCallback(mHostCallback);
                 mDockManager.addListener(mDockEventListener);
@@ -417,7 +463,7 @@
                 mWantSensors = true;
                 mWantTouchScreenSensors = true;
                 if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
-                    onWakeScreen(false, newState);
+                    onWakeScreen(false, newState, DozeLog.REASON_SENSOR_WAKE_UP);
                 }
                 break;
             case DOZE_AOD_PAUSED:
@@ -437,6 +483,7 @@
                 mDozeSensors.requestTemporaryDisable();
                 break;
             case FINISH:
+                cancelQuickPickupDelayableDoze();
                 mBroadcastReceiver.unregister(mBroadcastDispatcher);
                 mDozeHost.removeCallback(mHostCallback);
                 mDockManager.removeListener(mDockEventListener);
@@ -460,6 +507,17 @@
         mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
     }
 
+    /**
+     * Cancels last scheduled Runnable that transitions to STATE_DOZE (blank screen) after
+     * going into STATE_AOD (AOD screen) from the quick pickup gesture.
+     */
+    private void cancelQuickPickupDelayableDoze() {
+        if (mQuickPickupDozeCancellable != null) {
+            mQuickPickupDozeCancellable.run();
+            mQuickPickupDozeCancellable = null;
+        }
+    }
+
     private void checkTriggersAtInit() {
         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
                 || mDozeHost.isBlockingDoze()
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 1f67276..461a730 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2220,7 +2220,7 @@
         private void showControls(ControlsUiController controller) {
             mControlsUiController = controller;
             mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
-                    true /* startedFromGlobalActions */);
+                    null /* activityContext */);
         }
 
         private boolean isWalletViewAvailable() {
@@ -2449,7 +2449,7 @@
             });
             if (mControlsUiController != null) {
                 mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
-                        true /* startedFromGlobalActions */);
+                        null /* activityContext */);
             }
 
             mBackgroundDrawable.setAlpha(0);
@@ -2625,7 +2625,7 @@
             mGlobalActionsLayout.updateList();
             if (mControlsUiController != null) {
                 mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
-                        true /* startedFromGlobalActions */);
+                        null /* activityContext */);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 17f7ccf..97803c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -149,9 +149,14 @@
                        RemoteAnimationTarget[] wallpapers,
                         RemoteAnimationTarget[] nonApps,
                         IRemoteAnimationFinishedCallback finishedCallback) {
-            // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
-            // run animation.
             try {
+                if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
+                    mBinder.setOccluded(true /* isOccluded */, true /* animate */);
+                } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
+                    mBinder.setOccluded(false /* isOccluded */, true /* animate */);
+                }
+                // TODO(bc-unlock): Implement occlude/unocclude animation applied on apps,
+                //  wallpapers and nonApps.
                 finishedCallback.onAnimationFinished();
             } catch (RemoteException e) {
                 Slog.e(TAG, "RemoteException");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5536237..c3a523f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -35,6 +35,7 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -85,6 +86,7 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.KeyEvent;
@@ -211,7 +213,9 @@
     private Locale mLocale;
     private int mLayoutDirection;
 
+    private boolean mAllowForceNavBarHandleOpaque;
     private boolean mForceNavBarHandleOpaque;
+    private Optional<Long> mHomeButtonLongPressDurationMs;
     private boolean mIsCurrentUserSetup;
 
     /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
@@ -333,13 +337,23 @@
                 // If the current user is not yet setup, then don't update any button alphas
                 return;
             }
+            if (QuickStepContract.isLegacyMode(mNavBarMode)) {
+                // Don't allow the bar buttons to be affected by the alpha
+                return;
+            }
+
             ButtonDispatcher buttonDispatcher = null;
             boolean forceVisible = false;
-            if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
-                buttonDispatcher = mNavigationBarView.getBackButton();
-            } else if (QuickStepContract.isGesturalMode(mNavBarMode)) {
-                forceVisible = mForceNavBarHandleOpaque;
+            if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+                // Disallow home handle animations when in gestural
+                animate = false;
+                forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
                 buttonDispatcher = mNavigationBarView.getHomeHandle();
+                if (getBarTransitions() != null) {
+                    getBarTransitions().setBackgroundOverrideAlpha(alpha);
+                }
+            } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
+                buttonDispatcher = mNavigationBarView.getBackButton();
             }
             if (buttonDispatcher != null) {
                 buttonDispatcher.setVisibility(
@@ -373,6 +387,14 @@
 
     private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
 
+    private final Runnable mOnVariableDurationHomeLongClick = () -> {
+        if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) {
+            mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback(
+                    HapticFeedbackConstants.LONG_PRESS,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+        }
+    };
+
     private final ContentObserver mAssistContentObserver = new ContentObserver(
             new Handler(Looper.getMainLooper())) {
         @Override
@@ -394,6 +416,13 @@
                         mForceNavBarHandleOpaque = properties.getBoolean(
                                 NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
                     }
+
+                    if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
+                        mHomeButtonLongPressDurationMs = Optional.of(
+                            properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
+                        ).filter(duration -> duration != 0);
+                        reconfigureHomeLongClick();
+                    }
                 }
             };
 
@@ -506,10 +535,17 @@
         // Respect the latest disabled-flags.
         mCommandQueue.recomputeDisableFlags(mDisplayId, false);
 
+        mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
+                R.bool.allow_force_nav_bar_handle_opaque);
         mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 NAV_BAR_HANDLE_FORCE_OPAQUE,
                 /* defaultValue = */ true);
+        mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
+            DeviceConfig.NAMESPACE_SYSTEMUI,
+            HOME_BUTTON_LONG_PRESS_DURATION_MS,
+            /* defaultValue = */ 0
+        )).filter(duration -> duration != 0);
         DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
 
@@ -769,6 +805,22 @@
         }
     }
 
+    private void reconfigureHomeLongClick() {
+        if (mNavigationBarView == null
+                || mNavigationBarView.getHomeButton().getCurrentView() == null) {
+            return;
+        }
+        if (mHomeButtonLongPressDurationMs.isPresent()) {
+            mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
+            mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
+            mNavigationBarView.getHomeButton().setOnLongClickListener(null);
+        } else {
+            mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true);
+            mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true);
+            mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
+        }
+    }
+
     private int deltaRotation(int oldRotation, int newRotation) {
         int delta = newRotation - oldRotation;
         if (delta < 0) delta += 4;
@@ -779,6 +831,7 @@
         pw.println("NavigationBar (displayId=" + mDisplayId + "):");
         pw.println("  mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
         pw.println("  mCurrentRotation=" + mCurrentRotation);
+        pw.println("  mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
 
         if (mNavigationBarView != null) {
             pw.println("  mNavigationBarWindowState="
@@ -1108,7 +1161,8 @@
 
         ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
         homeButton.setOnTouchListener(this::onHomeTouch);
-        homeButton.setOnLongClickListener(this::onHomeLongClick);
+
+        reconfigureHomeLongClick();
 
         ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
         accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -1118,7 +1172,8 @@
         updateScreenPinningGestures();
     }
 
-    private boolean onHomeTouch(View v, MotionEvent event) {
+    @VisibleForTesting
+    boolean onHomeTouch(View v, MotionEvent event) {
         if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
             return true;
         }
@@ -1138,9 +1193,13 @@
                         return true;
                     }
                 }
+                mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+                    mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+                });
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
+                mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
                 mStatusBarLazy.get().awakenDreams();
                 break;
         }
@@ -1468,6 +1527,12 @@
     @Override
     public void onNavigationModeChanged(int mode) {
         mNavBarMode = mode;
+        if (!QuickStepContract.isGesturalMode(mode)) {
+            // Reset the override alpha
+            if (getBarTransitions() != null) {
+                getBarTransitions().setBackgroundOverrideAlpha(1f);
+            }
+        }
         updateScreenPinningGestures();
 
         if (!canShowSecondaryHandle()) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4491cc1..0bfd065 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -212,6 +212,14 @@
         createNavigationBar(display, null /* savedState */, null /* result */);
     }
 
+    @Override
+    public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        final NavigationBarView navigationBarView = getNavigationBarView(displayId);
+        if (navigationBarView != null) {
+            navigationBarView.setNavigationBarLumaSamplingEnabled(enable);
+        }
+    }
+
     /**
      * Recreates the navigation bar for the given display.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 61e1d61..fbc7c92 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -135,6 +136,10 @@
         mBarBackground.setFrame(frame);
     }
 
+    void setBackgroundOverrideAlpha(float alpha) {
+        mBarBackground.setOverrideAlpha(alpha);
+    }
+
     @Override
     protected boolean isLightsOut(int mode) {
         return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
@@ -230,4 +235,17 @@
     public void removeDarkIntensityListener(DarkIntensityListener listener) {
         mDarkIntensityListeners.remove(listener);
     }
+
+    public void dump(PrintWriter pw) {
+        pw.println("NavigationBarTransitions:");
+        pw.println("  mMode: " + getMode());
+        pw.println("  mAlwaysOpaque: " + isAlwaysOpaque());
+        pw.println("  mAllowAutoDimWallpaperNotVisible: " + mAllowAutoDimWallpaperNotVisible);
+        pw.println("  mWallpaperVisible: " + mWallpaperVisible);
+        pw.println("  mLightsOut: " + mLightsOut);
+        pw.println("  mAutoDim: " + mAutoDim);
+        pw.println("  bg overrideAlpha: " + mBarBackground.getOverrideAlpha());
+        pw.println("  bg color: " + mBarBackground.getColor());
+        pw.println("  bg frame: " + mBarBackground.getFrame());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 35d5ca9..091b17d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1158,6 +1158,8 @@
             int frameHeight = getResources().getDimensionPixelSize(
                     com.android.internal.R.dimen.navigation_bar_frame_height);
             mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
+        } else {
+            mBarTransitions.setBackgroundFrame(null);
         }
 
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -1331,6 +1333,7 @@
         if (mNavigationInflaterView != null) {
             mNavigationInflaterView.dump(pw);
         }
+        mBarTransitions.dump(pw);
         mContextualButtonGroup.dump(pw);
         mRecentsOnboarding.dump(pw);
         mRegionSamplingHelper.dump(pw);
@@ -1397,4 +1400,12 @@
     private final Consumer<Rect> mPipListener = bounds -> post(() -> {
         mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
     });
+
+    void setNavigationBarLumaSamplingEnabled(boolean enable) {
+        if (enable) {
+            mRegionSamplingHelper.start(mSamplingBounds);
+        } else {
+            mRegionSamplingHelper.stop();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a69ec27..378e49d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -35,6 +35,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
+import com.android.systemui.people.widget.PeopleTileKey;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.List;
@@ -117,7 +118,7 @@
             String pkg = tile.getPackageName();
             String status =
                     PeopleSpaceUtils.getLastInteractionString(mContext,
-                            tile.getLastInteractionTimestamp(), true);
+                            tile.getLastInteractionTimestamp());
             tileView.setStatus(status);
 
             tileView.setName(tile.getUserName().toString());
@@ -138,7 +139,9 @@
                         + mAppWidgetId);
             }
         }
-        mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId);
+        PeopleTileKey key = new PeopleTileKey(
+                tile.getId(), tile.getUserHandle().getIdentifier(), tile.getPackageName());
+        mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key);
         finishActivity();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 502c95c..aa45178 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -52,9 +52,7 @@
 import android.icu.util.Measure;
 import android.icu.util.MeasureUnit;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Parcelable;
-import android.os.ServiceManager;
 import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.service.notification.ConversationChannelWrapper;
@@ -71,10 +69,11 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.ArrayUtils;
 import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.people.widget.AppWidgetOptionsHelper;
 import com.android.systemui.people.widget.LaunchConversationActivity;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.people.widget.PeopleTileKey;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
@@ -106,7 +105,6 @@
     private static final int DAYS_IN_A_WEEK = 7;
     private static final int MIN_HOUR = 1;
     private static final int ONE_DAY = 1;
-    public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
     public static final String PACKAGE_NAME = "package_name";
     public static final String USER_ID = "user_id";
     public static final String SHORTCUT_ID = "shortcut_id";
@@ -115,6 +113,9 @@
     public static final int INVALID_WIDGET_ID = -1;
     public static final int INVALID_USER_ID = -1;
 
+    public static final PeopleTileKey EMPTY_KEY =
+            new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING);
+
     private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
     private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
     private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
@@ -200,67 +201,76 @@
             AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
         for (int appWidgetId : appWidgetIds) {
-            PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
-                    appWidgetId);
+            PeopleSpaceTile tile = getPeopleSpaceTile(
+                    context, appWidgetId, appWidgetManager, peopleManager);
             if (tile == null) {
                 if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
                 //TODO: Delete app widget id when crash is fixed (b/172932636)
                 continue;
             }
-
-            if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
-            RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
-            // Tell the AppWidgetManager to perform an update on the current app widget.
-            appWidgetManager.updateAppWidget(appWidgetId, views);
-
+            updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, tile);
             widgetIdToTile.put(appWidgetId, tile);
         }
         getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
     }
 
+    /**
+     * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. If the PeopleSpaceTile
+     * isn't cached, store it in AppWidgetOptions.
+     */
     @Nullable
-    public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
-            AppWidgetManager appWidgetManager,
-            Context context, int appWidgetId) {
+    public static PeopleSpaceTile getPeopleSpaceTile(Context context, int appWidgetId,
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
+        // First, check if tile is cached in AppWidgetOptions.
+        PeopleSpaceTile tile = AppWidgetOptionsHelper.getPeopleTile(appWidgetManager, appWidgetId);
+        if (tile != null) {
+            if (DEBUG) Log.d(TAG, "People Tile is cached for widget: " + appWidgetId);
+            return tile;
+        }
+
+        // If not, we get the PeopleTileKey from SharedPreferences, retrieve the Conversation from
+        // persisted storage, and cache it in AppWidgetOptions.
+        SharedPreferences widgetSp = context.getSharedPreferences(
+                String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        PeopleTileKey sharedPreferencesKey = new PeopleTileKey(
+                widgetSp.getString(SHORTCUT_ID, EMPTY_STRING),
+                widgetSp.getInt(USER_ID, INVALID_USER_ID),
+                widgetSp.getString(PACKAGE_NAME, EMPTY_STRING));
+
+        if (!sharedPreferencesKey.isValid()) {
+            Log.e(TAG, "Cannot find shortcut info for widgetId: " + appWidgetId);
+            return null;
+        }
+
+        if (DEBUG) Log.d(TAG, "PeopleTile key is present in sharedPreferences: " + appWidgetId);
+        // If tile is null, we need to retrieve from persisted storage.
+        return getPeopleTileFromPersistentStorage(context, sharedPreferencesKey, peopleManager);
+    }
+
+    /**
+     * Returns a {@link PeopleSpaceTile} based on {@link ConversationChannel} returned by
+     * {@link IPeopleManager}.
+     */
+    public static PeopleSpaceTile getPeopleTileFromPersistentStorage(Context context,
+            PeopleTileKey peopleTileKey, IPeopleManager peopleManager) {
         try {
-            // Migrate storage for existing users.
-            SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
-                    Context.MODE_PRIVATE);
-            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 (!validKey(shortcutId, pkg, userId)) {
-                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
-                shortcutId = sp.getString(String.valueOf(appWidgetId), null);
-                if (shortcutId == null) {
-                    Log.e(TAG, "Cannot restore widget");
-                    return null;
-                }
-                migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
-                pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
-                userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
-            }
-
-            // Check if tile is cached.
-            Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-            PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
-            if (tile != null) {
-                return tile;
-            }
-
-            // If tile is null, we need to retrieve from persisted storage.
-            if (DEBUG) {
-                Log.d(TAG,
-                        "Retrieving from storage after reboots: " + shortcutId + " user: " + userId
-                                + " pkg: " + pkg);
-            }
+            if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + peopleTileKey.toString());
             LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-            ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+            if (launcherApps == null) {
+                Log.d(TAG, "LauncherApps is null");
+                return null;
+            }
+
+            ConversationChannel channel = peopleManager.getConversation(
+                    peopleTileKey.getPackageName(),
+                    peopleTileKey.getUserId(),
+                    peopleTileKey.getShortcutId());
             if (channel == null) {
                 Log.d(TAG, "Could not retrieve conversation from storage");
                 return null;
             }
+
             return new PeopleSpaceTile.Builder(channel, launcherApps).build();
         } catch (Exception e) {
             Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
@@ -269,79 +279,46 @@
     }
 
     /** 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) {
+    public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) {
+        if (!key.isValid()) {
             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,
-            int appWidgetId) {
-        try {
-            List<PeopleSpaceTile> tiles =
-                    PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
-                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
-                            IPeopleManager.Stub.asInterface(
-                                    ServiceManager.getService(Context.PEOPLE_SERVICE)),
-                            context.getSystemService(LauncherApps.class),
-                            Dependency.get(NotificationEntryManager.class));
-            Optional<PeopleSpaceTile> entry = tiles.stream().filter(
-                    e -> e.getId().equals(shortcutId)).findFirst();
-            if (entry.isPresent()) {
-                if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
-                setStorageForTile(context, entry.get(), appWidgetId);
-            } else {
-                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");
-        }
+        return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>()));
     }
 
     /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
-    public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+    public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
+            int appWidgetId) {
         // Write relevant persisted storage.
         SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
                 Context.MODE_PRIVATE);
         SharedPreferences.Editor widgetEditor = widgetSp.edit();
-        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
-        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
-        int userId = getUserId(tile);
-        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName());
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId());
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId());
         widgetEditor.apply();
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
         SharedPreferences.Editor editor = sp.edit();
-        editor.putString(String.valueOf(appWidgetId), tile.getId());
-        String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
-        // Don't overwrite existing widgets with the same key.
-        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
-        storedWidgetIds.add(String.valueOf(appWidgetId));
-        editor.putStringSet(key, storedWidgetIds);
-        editor.apply();
+        editor.putString(String.valueOf(appWidgetId), key.getShortcutId());
 
-        // Write cached storage.
-        updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
-                tile);
+        // Don't overwrite existing widgets with the same key.
+        Set<String> storedWidgetIds = new HashSet<>(
+                sp.getStringSet(key.toString(), new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(appWidgetId));
+        editor.putStringSet(key.toString(), storedWidgetIds);
+        editor.apply();
     }
 
     /** Removes stored data when tile is deleted. */
-    public static void removeStorageForTile(Context context, String key, int widgetId) {
+    public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
+            int widgetId) {
         // Delete widgetId mapping to key.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
         SharedPreferences.Editor editor = sp.edit();
-        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        Set<String> storedWidgetIds = new HashSet<>(
+                sp.getStringSet(key.toString(), new HashSet<>()));
         storedWidgetIds.remove(String.valueOf(widgetId));
-        editor.putStringSet(key, storedWidgetIds);
+        editor.putStringSet(key.toString(), storedWidgetIds);
         editor.remove(String.valueOf(widgetId));
         editor.apply();
 
@@ -361,12 +338,12 @@
             Log.w(TAG, "NotificationEntryManager is null");
             return tiles;
         }
-        Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
+        Map<PeopleTileKey, NotificationEntry> visibleNotifications = notificationEntryManager
                 .getVisibleNotifications()
                 .stream()
                 .filter(entry -> entry.getRanking() != null
                         && entry.getRanking().getConversationShortcutInfo() != null)
-                .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
+                .collect(Collectors.toMap(PeopleTileKey::new, e -> e));
         if (DEBUG) {
             Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
         }
@@ -378,16 +355,15 @@
     }
 
     static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context,
-            PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
-        String shortcutId = tile.getId();
-        String packageName = tile.getPackageName();
-        int userId = getUserId(tile);
-        String key = getKey(shortcutId, packageName, userId);
+            PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) {
+        PeopleTileKey key = new PeopleTileKey(
+                tile.getId(), getUserId(tile), tile.getPackageName());
+
         if (!visibleNotifications.containsKey(key)) {
-            if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
+            if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString());
             return tile;
         }
-        if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
+        if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key.toString());
         return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
     }
 
@@ -423,17 +399,6 @@
                 .build();
     }
 
-    private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
-            PeopleSpaceTile tile) {
-        if (tile == null) {
-            if (DEBUG) Log.d(TAG, "Requested to store null tile");
-            return;
-        }
-        Bundle newOptions = new Bundle();
-        newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, tile);
-        appWidgetManager.updateAppWidgetOptions(appWidgetId, newOptions);
-    }
-
     /** Creates a {@link RemoteViews} for {@code tile}. */
     public static RemoteViews createRemoteViews(Context context,
             PeopleSpaceTile tile, int appWidgetId) {
@@ -653,7 +618,7 @@
         }
         // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile.
         views.setTextViewText(R.id.subtext, PeopleSpaceUtils.getLastInteractionString(
-                context, tile.getLastInteractionTimestamp(), false));
+                context, tile.getLastInteractionTimestamp()));
         return views;
     }
 
@@ -662,7 +627,7 @@
         RemoteViews views = new RemoteViews(
                 context.getPackageName(), R.layout.people_space_large_avatar_tile);
         String status = PeopleSpaceUtils.getLastInteractionString(
-                context, tile.getLastInteractionTimestamp(), true);
+                context, tile.getLastInteractionTimestamp());
         views.setTextViewText(R.id.last_interaction, status);
         return views;
     }
@@ -808,8 +773,7 @@
     }
 
     /** Returns a readable status describing the {@code lastInteraction}. */
-    public static String getLastInteractionString(Context context, long lastInteraction,
-            boolean includeLastChatted) {
+    public static String getLastInteractionString(Context context, long lastInteraction) {
         if (lastInteraction == 0L) {
             Log.e(TAG, "Could not get valid last interaction");
             return context.getString(R.string.basic_status);
@@ -818,41 +782,20 @@
         Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
         MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
                 MeasureFormat.FormatWidth.WIDE);
-        MeasureFormat shortFormatter = MeasureFormat.getInstance(Locale.getDefault(),
-                MeasureFormat.FormatWidth.SHORT);
         if (durationSinceLastInteraction.toHours() < MIN_HOUR) {
-            if (includeLastChatted) {
-                return context.getString(R.string.last_interaction_status_less_than,
-                        formatter.formatMeasures(new Measure(MIN_HOUR, MeasureUnit.HOUR)));
-            }
-            return context.getString(R.string.timestamp, shortFormatter.formatMeasures(
+            return context.getString(R.string.timestamp, formatter.formatMeasures(
                     new Measure(durationSinceLastInteraction.toMinutes(), MeasureUnit.MINUTE)));
         } else if (durationSinceLastInteraction.toDays() < ONE_DAY) {
-            if (includeLastChatted) {
-                return context.getString(R.string.last_interaction_status,
-                        formatter.formatMeasures(
-                                new Measure(durationSinceLastInteraction.toHours(),
-                                        MeasureUnit.HOUR)));
-            }
-            return context.getString(R.string.timestamp, shortFormatter.formatMeasures(
+            return context.getString(R.string.timestamp, formatter.formatMeasures(
                     new Measure(durationSinceLastInteraction.toHours(),
                             MeasureUnit.HOUR)));
         } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
-            if (includeLastChatted) {
-                return context.getString(R.string.last_interaction_status,
-                        formatter.formatMeasures(
-                                new Measure(durationSinceLastInteraction.toDays(),
-                                        MeasureUnit.DAY)));
-            }
-            return context.getString(R.string.timestamp, shortFormatter.formatMeasures(
+            return context.getString(R.string.timestamp, formatter.formatMeasures(
                     new Measure(durationSinceLastInteraction.toHours(),
                             MeasureUnit.DAY)));
         } else {
             return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
-                            ? (includeLastChatted ? R.string.last_interaction_status :
-                            R.string.timestamp) :
-                            (includeLastChatted ? R.string.last_interaction_status_over
-                                    : R.string.over_timestamp),
+                            ? R.string.timestamp : R.string.over_timestamp,
                     formatter.formatMeasures(
                             new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
                                     MeasureUnit.WEEK)));
@@ -957,11 +900,15 @@
         removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId);
     }
 
-    /** Update app widget options and the current view. */
+    /** Updates tile in app widget options and the current view. */
     public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
             Context context, int appWidgetId, PeopleSpaceTile tile) {
-        updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
+        AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile);
+
+        if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
         RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+        // Tell the AppWidgetManager to perform an update on the current app widget.
         appWidgetManager.updateAppWidget(appWidgetId, views);
     }
 
@@ -1006,44 +953,6 @@
         return lookupKeysWithBirthdaysToday;
     }
 
-    static String getKey(NotificationEntry entry) {
-        if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null
-                || entry.getSbn() == null || entry.getSbn().getUser() == null) {
-            return null;
-        }
-        return getKey(entry.getRanking().getConversationShortcutInfo().getId(),
-                entry.getSbn().getPackageName(),
-                entry.getSbn().getUser().getIdentifier());
-    }
-
-    /**
-     * Returns the uniquely identifying key for the conversation.
-     *
-     * <p>{@code userId} will always be a number, so we put user ID as the
-     * delimiter between the app-provided strings of shortcut ID and package name.
-     *
-     * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
-     * a {@code packageName} to always start with a letter. This restriction means we are
-     * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
-     * case is impossible given the package name restrictions:
-     * <ul>
-     *     <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
-     *     <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/AppWidgetOptionsHelper.java b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java
new file mode 100644
index 0000000..df08ee4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java
@@ -0,0 +1,91 @@
+/*
+ * 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.people.widget;
+
+import static com.android.systemui.people.PeopleSpaceUtils.DEBUG;
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_KEY;
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
+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 android.app.people.PeopleSpaceTile;
+import android.appwidget.AppWidgetManager;
+import android.os.Bundle;
+import android.util.Log;
+
+/** Helper class encapsulating AppWidgetOptions for People Tile. */
+public class AppWidgetOptionsHelper {
+    private static final String TAG = "AppWidgetOptionsHelper";
+
+    /** Key to store {@link PeopleSpaceTile} in AppWidgetOptions Bundle. */
+    public static final String OPTIONS_PEOPLE_TILE = "options_people_tile";
+
+    /** Sets {@link PeopleSpaceTile} in AppWidgetOptions. */
+    public static void setPeopleTile(AppWidgetManager appWidgetManager, int appWidgetId,
+            PeopleSpaceTile tile) {
+        if (tile == null) {
+            if (DEBUG) Log.d(TAG, "Requested to store null tile");
+            return;
+        }
+        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+        options.putParcelable(OPTIONS_PEOPLE_TILE, tile);
+        appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
+    }
+
+    /** Gets {@link PeopleSpaceTile} from AppWidgetOptions. */
+    public static PeopleSpaceTile getPeopleTile(AppWidgetManager appWidgetManager,
+            int appWidgetId) {
+        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+        return options != null ? options.getParcelable(OPTIONS_PEOPLE_TILE) : null;
+    }
+
+    /** Sets {@link PeopleTileKey} in AppWidgetOptions. */
+    public static void setPeopleTileKey(AppWidgetManager appWidgetManager, int appWidgetId,
+            PeopleTileKey key) {
+        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+        options.putString(SHORTCUT_ID, key.getShortcutId());
+        options.putInt(USER_ID, key.getUserId());
+        options.putString(PACKAGE_NAME, key.getPackageName());
+        appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
+    }
+
+    /** Gets {@link PeopleTileKey} from AppWidgetOptions. */
+    public static PeopleTileKey getPeopleTileKey(AppWidgetManager appWidgetManager,
+            int appWidgetId) {
+        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+        if (options == null) {
+            return EMPTY_KEY;
+        }
+        return getPeopleTileKeyFromBundle(options);
+    }
+
+    /** Gets {@link PeopleTileKey} from Bundle {@code options}. */
+    public static PeopleTileKey getPeopleTileKeyFromBundle(Bundle options) {
+        String pkg = options.getString(PACKAGE_NAME, EMPTY_STRING);
+        int userId = options.getInt(USER_ID, INVALID_USER_ID);
+        String shortcutId = options.getString(SHORTCUT_ID, EMPTY_STRING);
+        return new PeopleTileKey(shortcutId, userId, pkg);
+    }
+
+    /** Removes {@link PeopleTileKey} from AppWidgetOptions. */
+    public static void removePeopleTileKey(AppWidgetManager appWidgetManager,
+            int appWidgetId) {
+        setPeopleTileKey(appWidgetManager, appWidgetId, EMPTY_KEY);
+    }
+}
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 22ee9e8..7da9a80 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.people.widget;
 
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
 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;
@@ -38,6 +39,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.preference.PreferenceManager;
@@ -78,8 +80,8 @@
     private PeopleManager mPeopleManager;
     public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
     @GuardedBy("mLock")
-    public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners =
-            new HashMap<>();
+    public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener>
+            mListeners = new HashMap<>();
 
     @Inject
     public PeopleSpaceWidgetManager(Context context) {
@@ -122,8 +124,8 @@
                     Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
             if (showSingleConversation) {
                 synchronized (mLock) {
-                    PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
-                            mAppWidgetManager, mIPeopleManager);
+                    PeopleSpaceUtils.updateSingleConversationWidgets(
+                            mContext, widgetIds, mAppWidgetManager, mIPeopleManager);
                 }
             }
         } catch (Exception e) {
@@ -157,9 +159,11 @@
                 return;
             }
             synchronized (mLock) {
-                Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId,
-                        sbn.getPackageName(),
-                        UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier());
+                PeopleTileKey key = new PeopleTileKey(
+                        sbnShortcutId,
+                        UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(),
+                        sbn.getPackageName());
+                Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key);
                 for (String widgetIdString : storedWidgetIds) {
                     int widgetId = Integer.parseInt(widgetIdString);
                     if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
@@ -177,9 +181,9 @@
     public void updateWidgetsWithConversationChanged(ConversationChannel conversation) {
         ShortcutInfo info = conversation.getShortcutInfo();
         synchronized (mLock) {
-            Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(),
-                    info.getPackage(),
-                    info.getUserId());
+            PeopleTileKey key = new PeopleTileKey(
+                    info.getId(), info.getUserId(), info.getPackage());
+            Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key);
             for (String widgetIdString : storedWidgetIds) {
                 if (DEBUG) {
                     Log.d(TAG,
@@ -197,9 +201,8 @@
      */
     private void updateStorageAndViewWithConversationData(ConversationChannel conversation,
             int appWidgetId) {
-        PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
-                mContext,
-                appWidgetId);
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(
+                mContext, appWidgetId, mAppWidgetManager, mIPeopleManager);
         if (storedTile == null) {
             if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to");
             return;
@@ -232,9 +235,8 @@
             StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction,
             int appWidgetId) {
-        PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
-                mContext,
-                appWidgetId);
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(
+                mContext, appWidgetId, mAppWidgetManager, mIPeopleManager);
         if (storedTile == null) {
             if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
             return;
@@ -312,18 +314,39 @@
         }
     };
 
-    /** Adds {@code tile} mapped to {@code appWidgetId}. */
-    public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) {
+    /**
+     * Checks if this widget has been added externally, and this the first time we are learning
+     * about the widget. If so, the widget adder should have populated options with PeopleTileKey
+     * arguments.
+     */
+    public void onAppWidgetOptionsChanged(int appWidgetId, Bundle newOptions) {
+        // Check if this widget has been added externally, and this the first time we are
+        // learning about the widget. If so, the widget adder should have populated options with
+        // PeopleTileKey arguments.
+        if (DEBUG) Log.d(TAG, "onAppWidgetOptionsChanged called for widget: " + appWidgetId);
+        PeopleTileKey optionsKey = AppWidgetOptionsHelper.getPeopleTileKeyFromBundle(newOptions);
+        if (optionsKey.isValid()) {
+            if (DEBUG) {
+                Log.d(TAG, "PeopleTileKey was present in Options, shortcutId: "
+                        + optionsKey.getShortcutId());
+            }
+            addNewWidget(appWidgetId, optionsKey);
+            AppWidgetOptionsHelper.removePeopleTileKey(mAppWidgetManager, appWidgetId);
+        }
+    }
+
+    /** Adds{@code tile} mapped to {@code appWidgetId}. */
+    public void addNewWidget(int appWidgetId, PeopleTileKey key) {
         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);
+            if (DEBUG) Log.d(TAG, "Add storage for : " + key.getShortcutId());
+            PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, 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);
+            if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + key.getShortcutId());
+            mLauncherApps.cacheShortcuts(key.getPackageName(),
+                    Collections.singletonList(key.getShortcutId()),
+                    UserHandle.of(key.getUserId()), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
         } catch (Exception e) {
             Log.w(TAG, "Exception caching shortcut:" + e);
         }
@@ -335,19 +358,16 @@
     public void registerConversationListenerIfNeeded(int widgetId,
             PeopleSpaceWidgetProvider.TileConversationListener newListener) {
         // Retrieve storage needed for registration.
-        String packageName;
-        String shortcutId;
-        int userId;
-        String key;
+        PeopleTileKey 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);
+            key = new PeopleTileKey(
+                    widgetSp.getString(SHORTCUT_ID, EMPTY_STRING),
+                    widgetSp.getInt(USER_ID, INVALID_USER_ID),
+                    widgetSp.getString(PACKAGE_NAME, EMPTY_STRING));
+            if (!key.isValid()) {
+                if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId);
                 return;
             }
         }
@@ -359,9 +379,9 @@
             if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key);
             mListeners.put(key, newListener);
         }
-        mPeopleManager.registerConversationListener(packageName,
-                userId,
-                shortcutId, newListener,
+        mPeopleManager.registerConversationListener(key.getPackageName(),
+                key.getUserId(),
+                key.getShortcutId(), newListener,
                 mContext.getMainExecutor());
     }
 
@@ -371,27 +391,24 @@
             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;
+            PeopleTileKey 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) {
+                key = new PeopleTileKey(
+                        widgetSp.getString(SHORTCUT_ID, null),
+                        widgetSp.getInt(USER_ID, INVALID_USER_ID),
+                        widgetSp.getString(PACKAGE_NAME, null));
+                if (!key.isValid()) {
                     if (DEBUG) Log.e(TAG, "Could not delete " + widgetId);
                     return;
                 }
                 storedWidgetIdsForKey = new HashSet<>(
-                        mSharedPrefs.getStringSet(key, new HashSet<>()));
+                        mSharedPrefs.getStringSet(key.toString(), new HashSet<>()));
             }
             synchronized (mLock) {
-                PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId);
+                PeopleSpaceUtils.removeSharedPreferencesStorageForTile(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());
@@ -399,13 +416,13 @@
                     && storedWidgetIdsForKey.size() == 1) {
                 if (DEBUG) Log.d(TAG, "Remove caching and listener");
                 unregisterConversationListener(key, widgetId);
-                uncacheConversationShortcut(shortcutId, packageName, userId);
+                uncacheConversationShortcut(key);
             }
         }
     }
 
     /** Unregisters the conversation listener for {@code appWidgetId}. */
-    private void unregisterConversationListener(String key, int appWidgetId) {
+    private void unregisterConversationListener(PeopleTileKey key, int appWidgetId) {
         PeopleSpaceWidgetProvider.TileConversationListener registeredListener;
         synchronized (mListeners) {
             registeredListener = mListeners.get(key);
@@ -420,12 +437,12 @@
     }
 
     /** Uncaches the conversation shortcut. */
-    private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) {
+    private void uncacheConversationShortcut(PeopleTileKey key) {
         try {
-            if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
-            mLauncherApps.uncacheShortcuts(packageName,
-                    Collections.singletonList(shortcutId),
-                    UserHandle.of(userId),
+            if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + key.getShortcutId());
+            mLauncherApps.uncacheShortcuts(key.getPackageName(),
+                    Collections.singletonList(key.getShortcutId()),
+                    UserHandle.of(key.getUserId()),
                     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 cccf7aa..3bc5b29 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -22,6 +22,7 @@
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.content.Context;
+import android.os.Bundle;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -67,18 +68,21 @@
         ensurePeopleSpaceWidgetManagerInitialized(context);
         peopleSpaceWidgetManager.updateWidgets(appWidgetIds);
         for (int appWidgetId : appWidgetIds) {
+            if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId);
             PeopleSpaceWidgetProvider.TileConversationListener
                     newListener = new PeopleSpaceWidgetProvider.TileConversationListener();
             peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId,
                     newListener);
         }
-        return;
     }
 
-    private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
-        if (peopleSpaceWidgetManager == null) {
-            peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
-        }
+    /** Called when widget updates. */
+    @Override
+    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
+            int appWidgetId, Bundle newOptions) {
+        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
+        ensurePeopleSpaceWidgetManagerInitialized(context);
+        peopleSpaceWidgetManager.onAppWidgetOptionsChanged(appWidgetId, newOptions);
     }
 
     @Override
@@ -88,6 +92,12 @@
         peopleSpaceWidgetManager.deleteWidgets(appWidgetIds);
     }
 
+    private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
+        if (peopleSpaceWidgetManager == null) {
+            peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
+        }
+    }
+
     @VisibleForTesting
     public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) {
         peopleSpaceWidgetManager = manager;
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index 0503522..87b2a15 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -106,7 +106,7 @@
             PeopleSpaceTile tile = mTiles.get(i);
 
             String status = PeopleSpaceUtils.getLastInteractionString(mContext,
-                    tile.getLastInteractionTimestamp(), true);
+                    tile.getLastInteractionTimestamp());
 
             personView.setTextViewText(R.id.status, status);
             personView.setTextViewText(R.id.name, tile.getUserName().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java
new file mode 100644
index 0000000..ac42cb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java
@@ -0,0 +1,105 @@
+/*
+ * 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.people.widget;
+
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
+
+import android.text.TextUtils;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.Objects;
+
+/** Class that encapsulates fields identifying a Conversation. */
+public class PeopleTileKey {
+    private String mShortcutId;
+    private int mUserId;
+    private String mPackageName;
+
+    public PeopleTileKey(String shortcutId, int userId, String packageName) {
+        mShortcutId = shortcutId;
+        mUserId = userId;
+        mPackageName = packageName;
+    }
+
+    public PeopleTileKey(NotificationEntry entry) {
+        mShortcutId = entry.getRanking() != null
+                && entry.getRanking().getConversationShortcutInfo() != null
+                ? entry.getRanking().getConversationShortcutInfo().getId()
+                : EMPTY_STRING;
+        mUserId = entry.getSbn().getUser() != null
+                ? entry.getSbn().getUser().getIdentifier() : INVALID_USER_ID;
+        mPackageName = entry.getSbn().getPackageName();
+    }
+
+    public String getShortcutId() {
+        return mShortcutId;
+    }
+
+    public int getUserId() {
+        return mUserId;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /** Returns whether PeopleTileKey is valid/well-formed. */
+    public boolean isValid() {
+        return !TextUtils.isEmpty(mShortcutId) && !TextUtils.isEmpty(mPackageName) && mUserId >= 0;
+    }
+
+    /**
+     * Returns the uniquely identifying key for the conversation.
+     *
+     * <p>{@code userId} will always be a number, so we put user ID as the
+     * delimiter between the app-provided strings of shortcut ID and package name.
+     *
+     * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+     * a {@code packageName} to always start with a letter. This restriction means we are
+     * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+     * case is impossible given the package name restrictions:
+     * <ul>
+     *     <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+     *     <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+     * </ul>
+     */
+    @Override
+    public String toString() {
+        if (!isValid()) return null;
+        return mShortcutId + "/" + mUserId + "/" + mPackageName;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof PeopleTileKey)) {
+            return false;
+        }
+        final PeopleTileKey o = (PeopleTileKey) other;
+        return Objects.equals(o.toString(), this.toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return mPackageName.hashCode() + Integer.valueOf(mUserId).hashCode()
+                + mShortcutId.hashCode();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index 03c1843..f87ea7c 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -214,9 +214,7 @@
 
     private fun filterType(type: PrivacyType?): PrivacyType? {
         return type?.let {
-            if (privacyItemController.allIndicatorsAvailable) {
-                it
-            } else if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
+            if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
                     privacyItemController.micCameraAvailable) {
                 it
             } else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 1e04516..03d6154 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -68,8 +68,6 @@
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
         }
         const val TAG = "PrivacyItemController"
-        private const val ALL_INDICATORS =
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
         private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
         private const val DEFAULT_ALL_INDICATORS = false
@@ -83,11 +81,6 @@
         @Synchronized get() = field.toList() // Returns a shallow copy of the list
         @Synchronized set
 
-    private fun isAllIndicatorsEnabled(): Boolean {
-        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
-    }
-
     private fun isMicCameraEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 MIC_CAMERA, DEFAULT_MIC_CAMERA)
@@ -120,34 +113,29 @@
         uiExecutor.execute(notifyChanges)
     }
 
-    var allIndicatorsAvailable = isAllIndicatorsEnabled()
-        private set
     var micCameraAvailable = isMicCameraEnabled()
         private set
     var locationAvailable = isLocationEnabled()
 
+    var allIndicatorsAvailable = micCameraAvailable && locationAvailable
+
     private val devicePropertiesChangedListener =
             object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
             if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
-                    (properties.keyset.contains(ALL_INDICATORS) ||
-                            properties.keyset.contains(MIC_CAMERA) ||
+                    (properties.keyset.contains(MIC_CAMERA) ||
                             properties.keyset.contains(LOCATION))) {
 
                 // Running on the ui executor so can iterate on callbacks
-                if (properties.keyset.contains(ALL_INDICATORS)) {
-                    allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS,
-                            DEFAULT_ALL_INDICATORS)
-                    callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
-                }
-
                 if (properties.keyset.contains(MIC_CAMERA)) {
                     micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
+                    allIndicatorsAvailable = micCameraAvailable && locationAvailable
                     callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
                 }
 
                 if (properties.keyset.contains(LOCATION)) {
                     locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+                    allIndicatorsAvailable = micCameraAvailable && locationAvailable
                     callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
                 }
                 internalUiExecutor.updateListeningState()
@@ -163,8 +151,7 @@
             active: Boolean
         ) {
             // Check if we care about this code right now
-            if (!allIndicatorsAvailable &&
-                    (code in OPS_LOCATION && !locationAvailable)) {
+            if (code in OPS_LOCATION && !locationAvailable) {
                 return
             }
             val userId = UserHandle.getUserId(uid)
@@ -231,7 +218,7 @@
      */
     private fun setListeningState() {
         val listen = !callbacks.isEmpty() and
-                (allIndicatorsAvailable || micCameraAvailable || locationAvailable)
+                (micCameraAvailable || locationAvailable)
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -338,7 +325,7 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
-        if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) {
+        if (type == PrivacyType.TYPE_LOCATION && !locationAvailable) {
             return null
         }
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 0fa7b59..5ab7bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -88,7 +88,6 @@
     private boolean mViewAndWindowAdded;
     private ObjectAnimator mAnimator;
 
-    private boolean mAllIndicatorsFlagEnabled;
     private boolean mMicCameraIndicatorFlagEnabled;
     private boolean mLocationIndicatorEnabled;
     private List<PrivacyItem> mPrivacyItems;
@@ -111,12 +110,10 @@
         mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin));
         mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
 
-        mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable();
         mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
         mLocationIndicatorEnabled = privacyItemController.getLocationAvailable();
 
         if (DEBUG) {
-            Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled);
             Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled);
             Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled);
         }
@@ -135,12 +132,6 @@
     }
 
     @Override
-    public void onFlagAllChanged(boolean flag) {
-        if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag);
-        mAllIndicatorsFlagEnabled = flag;
-    }
-
-    @Override
     public void onFlagMicCameraChanged(boolean flag) {
         if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag);
         mMicCameraIndicatorFlagEnabled = flag;
@@ -155,8 +146,8 @@
     private void updateUI() {
         if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
 
-        if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled
-                || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) {
+        if ((mMicCameraIndicatorFlagEnabled || mLocationIndicatorEnabled)
+                && !mPrivacyItems.isEmpty()) {
             if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) {
                 showIndicator();
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index fe76668..b8823e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.qs;
 
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -35,6 +37,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -77,6 +80,9 @@
     // This animates fading of SecurityFooter and media divider
     private TouchAnimator mAllPagesDelayedAnimator;
     private TouchAnimator mBrightnessAnimator;
+    private HeightExpansionAnimator mQQSTileHeightAnimator;
+    private HeightExpansionAnimator mOtherTilesExpandAnimator;
+
     private boolean mNeedsAnimatorUpdate = false;
 
     private boolean mOnKeyguard;
@@ -161,7 +167,7 @@
     @Override
     public void onViewAttachedToWindow(View v) {
         mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
-                MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
+                MOVE_FULL_ROWS);
     }
 
     @Override
@@ -179,9 +185,6 @@
             }
         } else if (MOVE_FULL_ROWS.equals(key)) {
             mFullRows = TunerService.parseIntegerSwitch(newValue, true);
-        } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
-            mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue);
-            clearAnimationState();
         }
         updateAnimators();
     }
@@ -209,6 +212,10 @@
         clearAnimationState();
         mAllViews.clear();
         mQuickQsViews.clear();
+        mQQSTileHeightAnimator = null;
+        mOtherTilesExpandAnimator = null;
+
+        mNumQuickTiles = mQuickQsPanel.getNumQuickTiles();
 
         QSTileLayout tileLayout = mQsPanelController.getTileLayout();
         mAllViews.add((View) tileLayout);
@@ -218,6 +225,9 @@
                 + mQs.getHeader().getPaddingBottom();
         firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
 
+        boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled();
+        int qqsTileHeight = 0;
+
         for (QSTile tile : tiles) {
             QSTileView tileView = mQsPanelController.getTileView(tile);
             if (tileView == null) {
@@ -237,22 +247,47 @@
                 getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
                 getRelativePosition(loc2, tileIcon, view);
                 final int xDiff = loc2[0] - loc1[0];
-                final int yDiff = loc2[1] - loc1[1];
-
+                int yDiff = loc2[1] - loc1[1];
 
                 if (count < tileLayout.getNumVisibleTiles()) {
+                    getRelativePosition(loc1, quickTileView, view);
+                    getRelativePosition(loc2, tileView, view);
+                    int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
                     // Move the quick tile right from its location to the new one.
-                    translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
-                    translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
-
-                    // Counteract the parent translation on the tile. So we have a static base to
-                    // animate the label position off from.
-                    //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+                    View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
+                    translationXBuilder.addFloat(v, "translationX", 0, xDiff);
+                    translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
+                    mAllViews.add(v);
 
                     // Move the real tile from the quick tile position to its final
                     // location.
-                    translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
-                    translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+                    v = qsSideLabelsEnabled ? tileIcon : tileView;
+                    translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
+                    translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
+
+                    if (qsSideLabelsEnabled) {
+                        translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
+                        translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+
+                        if (mQQSTileHeightAnimator == null) {
+                            mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+                                    quickTileView.getHeight(), tileView.getHeight());
+                            qqsTileHeight = quickTileView.getHeight();
+                        }
+
+                        mQQSTileHeightAnimator.addView(quickTileView);
+                        View qqsLabelContainer = quickTileView.getLabelContainer();
+                        View qsLabelContainer = tileView.getLabelContainer();
+
+                        getRelativePosition(loc1, qqsLabelContainer, view);
+                        getRelativePosition(loc2, qsLabelContainer, view);
+                        yDiff = loc2[1] - loc1[1] - yOffset;
+
+                        translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
+                        translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
+                        mAllViews.add(qqsLabelContainer);
+                        mAllViews.add(qsLabelContainer);
+                    }
 
                 } else { // These tiles disappear when expanding
                     firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
@@ -265,7 +300,7 @@
                             translationX);
                 }
 
-                if (mFeatureFlags.isQSLabelsEnabled()) {
+                if (qsSideLabelsEnabled) {
                     mQuickQsViews.add(tileView);
                 } else {
                     mQuickQsViews.add(tileView.getIconWithBackground());
@@ -278,9 +313,29 @@
 
                 mAllViews.add(tileIcon);
             } else {
-                firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
-                firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+                if (!qsSideLabelsEnabled) {
+                    firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
+                    firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+                } else {
+                    // Pretend there's a corresponding QQS tile (for the position) that we are
+                    // expanding from.
+                    SideLabelTileLayout qqsLayout =
+                            (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
+                    getRelativePosition(loc1, qqsLayout, view);
+                    getRelativePosition(loc2, tileView, view);
+                    int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
+                    translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
+                    if (mOtherTilesExpandAnimator == null) {
+                        mOtherTilesExpandAnimator =
+                                new HeightExpansionAnimator(
+                                        this, qqsTileHeight, tileView.getHeight());
+                    }
+                    mOtherTilesExpandAnimator.addView(tileView);
+                    tileView.setClipChildren(true);
+                    tileView.setClipToPadding(true);
+                }
             }
+
             mAllViews.add(tileView);
             count++;
         }
@@ -303,7 +358,7 @@
                     .build();
             // Fade in the tiles/labels as we reach the final position.
             Builder builder = new Builder()
-                    .setStartDelay(EXPANDED_TILE_DELAY)
+                    .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY)
                     .addFloat(tileLayout, "alpha", 0, 1);
             mFirstPageDelayedAnimator = builder.build();
 
@@ -331,6 +386,12 @@
             translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
             mTranslationXAnimator = translationXBuilder.build();
             mTranslationYAnimator = translationYBuilder.build();
+            if (mQQSTileHeightAnimator != null) {
+                mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+            }
+            if (mOtherTilesExpandAnimator != null) {
+                mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+            }
         }
         mNonfirstPageAnimator = new TouchAnimator.Builder()
                 .addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -404,6 +465,12 @@
             if (mBrightnessAnimator != null) {
                 mBrightnessAnimator.setPosition(position);
             }
+            if (mQQSTileHeightAnimator != null) {
+                mQQSTileHeightAnimator.setPosition(position);
+            }
+            if (mOtherTilesExpandAnimator != null) {
+                mOtherTilesExpandAnimator.setPosition(position);
+            }
         } else {
             mNonfirstPageAnimator.setPosition(position);
             mNonfirstPageDelayedAnimator.setPosition(position);
@@ -446,6 +513,16 @@
             v.setAlpha(1);
             v.setTranslationX(0);
             v.setTranslationY(0);
+            if (v instanceof SideLabelTileLayout) {
+                ((SideLabelTileLayout) v).setClipChildren(false);
+                ((SideLabelTileLayout) v).setClipToPadding(false);
+            }
+        }
+        if (mQQSTileHeightAnimator != null) {
+            mQQSTileHeightAnimator.resetViewsHeights();
+        }
+        if (mOtherTilesExpandAnimator != null) {
+            mOtherTilesExpandAnimator.resetViewsHeights();
         }
         final int N2 = mQuickQsViews.size();
         for (int i = 0; i < N2; i++) {
@@ -483,4 +560,61 @@
         updateAnimators();
         setCurrentPosition();
     };
+
+    static class HeightExpansionAnimator {
+        private final List<View> mViews = new ArrayList<>();
+        private final ValueAnimator mAnimator;
+        private final TouchAnimator.Listener mListener;
+
+        private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
+                new ValueAnimator.AnimatorUpdateListener() {
+            float mLastT = -1;
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                float t = valueAnimator.getAnimatedFraction();
+                if (t == 0f) {
+                    mListener.onAnimationAtStart();
+                } else if (t == 1f) {
+                    mListener.onAnimationAtEnd();
+                } else if (mLastT <= 0 || mLastT == 1) {
+                    mListener.onAnimationStarted();
+                }
+                mLastT = t;
+                final int viewCount = mViews.size();
+                int height = (Integer) valueAnimator.getAnimatedValue();
+                for (int i = 0; i < viewCount; i++) {
+                    View v = mViews.get(i);
+                    v.setBottom(v.getTop() + height);
+                }
+            }
+        };
+
+        HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) {
+            mListener = listener;
+            mAnimator = ValueAnimator.ofInt(startHeight, endHeight);
+            mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+            mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+            mAnimator.addUpdateListener(mUpdateListener);
+        }
+
+        void addView(View v) {
+            mViews.add(v);
+        }
+
+        void setInterpolator(TimeInterpolator interpolator) {
+            mAnimator.setInterpolator(interpolator);
+        }
+
+        void setPosition(float position) {
+            mAnimator.setCurrentFraction(position);
+        }
+
+        void resetViewsHeights() {
+            final int viewsCount = mViews.size();
+            for (int i = 0; i < viewsCount; i++) {
+                View v = mViews.get(i);
+                v.setBottom(v.getTop() + v.getMeasuredHeight());
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ed308ae..7c9f0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -430,6 +430,7 @@
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSPanelController.setRevealExpansion(expansion);
         mQSPanelController.getTileLayout().setExpansion(expansion);
+        mQuickQSPanelController.getTileLayout().setExpansion(expansion);
         mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ff9b912..c794a21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -315,7 +315,7 @@
         int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
         mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
                 R.dimen.qs_footer_horizontal_margin);
-        mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
+        mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f);
         updatePadding();
 
         updatePageIndicator();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f51d7ef..e7828c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -348,19 +348,20 @@
     }
 
     static class QQSSideLabelTileLayout extends SideLabelTileLayout {
+
         QQSSideLabelTileLayout(Context context) {
             super(context, null);
             setClipChildren(false);
             setClipToPadding(false);
             LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                     LayoutParams.WRAP_CONTENT);
-            lp.gravity = Gravity.CENTER_HORIZONTAL;
             setLayoutParams(lp);
             setMaxColumns(4);
         }
 
         @Override
         public boolean updateResources() {
+            mCellHeightResId = R.dimen.qs_quick_tile_size;
             boolean b = super.updateResources();
             mMaxAllowedRows = 2;
             return b;
@@ -379,5 +380,38 @@
             updateMaxRows(10000, mRecords.size());
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
+
+        @Override
+        public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+            boolean startedListening = !mListening && listening;
+            super.setListening(listening, uiEventLogger);
+            if (startedListening) {
+                // getNumVisibleTiles() <= mRecords.size()
+                for (int i = 0; i < getNumVisibleTiles(); i++) {
+                    QSTile tile = mRecords.get(i).tile;
+                    uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
+                            tile.getMetricsSpec(), tile.getInstanceId());
+                }
+            }
+        }
+
+        @Override
+        public void setExpansion(float expansion) {
+            if (expansion > 0f && expansion < 1f) {
+                return;
+            }
+            boolean selected = expansion == 0f;
+            // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
+            // point we want them to be selected so the tiles will marquee (but not at other points
+            // of expansion.
+            // We set it as not important while we change this, so setting each tile as selected
+            // will not cause them to announce themselves until the user has actually selected the
+            // item.
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            for (int i = 0; i < getChildCount(); i++) {
+                getChildAt(i).setSelected(selected);
+            }
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 671f8f7..fee56b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -99,7 +99,7 @@
                 break;
             }
         }
-        super.setTiles(tiles, !mQSLabelFlag);
+        super.setTiles(tiles, /* collapsedView */ true);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index e3c39aa..eedcdab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -97,7 +97,6 @@
 
     private boolean mListening;
     private AlarmClockInfo mNextAlarm;
-    private boolean mAllIndicatorsEnabled;
     private boolean mMicCameraIndicatorsEnabled;
     private boolean mLocationIndicatorsEnabled;
     private boolean mPrivacyChipLogged;
@@ -151,14 +150,6 @@
         }
 
         @Override
-        public void onFlagAllChanged(boolean flag) {
-            if (mAllIndicatorsEnabled != flag) {
-                mAllIndicatorsEnabled = flag;
-                update();
-            }
-        }
-
-        @Override
         public void onFlagMicCameraChanged(boolean flag) {
             if (mMicCameraIndicatorsEnabled != flag) {
                 mMicCameraIndicatorsEnabled = flag;
@@ -270,7 +261,6 @@
         mRingerContainer.setOnClickListener(mOnClickListener);
         mPrivacyChip.setOnClickListener(mOnClickListener);
 
-        mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
         mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
         mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
 
@@ -321,7 +311,6 @@
             mNextAlarmController.addCallback(mNextAlarmChangeCallback);
             mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
             // Get the most up to date info
-            mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
             mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
             mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
@@ -353,13 +342,13 @@
     private List<String> getIgnoredIconSlots() {
         ArrayList<String> ignored = new ArrayList<>();
         if (getChipEnabled()) {
-            if (mAllIndicatorsEnabled || mMicCameraIndicatorsEnabled) {
+            if (mMicCameraIndicatorsEnabled) {
                 ignored.add(mView.getResources().getString(
                         com.android.internal.R.string.status_bar_camera));
                 ignored.add(mView.getResources().getString(
                         com.android.internal.R.string.status_bar_microphone));
             }
-            if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
+            if (mLocationIndicatorsEnabled) {
                 ignored.add(mView.getResources().getString(
                         com.android.internal.R.string.status_bar_location));
             }
@@ -368,7 +357,7 @@
     }
 
     private boolean getChipEnabled() {
-        return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled;
+        return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
     }
 
     private boolean isZenOverridingRinger() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 52f111e7..0ebadfd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -33,4 +33,15 @@
     override fun isFull(): Boolean {
         return mRecords.size >= maxTiles()
     }
+
+    /**
+     * Return the position from the top of the layout of the tile with this index.
+     *
+     * This will return a position even for indices that go beyond [maxTiles], continuing the rows
+     * beyond that.
+     */
+    fun getPhantomTopPosition(index: Int): Int {
+        val row = index / mColumns
+        return getRowTop(row)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c1ce4a5..9e0aa5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -25,6 +25,7 @@
 
     protected int mColumns;
     protected int mCellWidth;
+    protected int mCellHeightResId = R.dimen.qs_tile_height;
     protected int mCellHeight;
     protected int mMaxCellHeight;
     protected int mCellMarginHorizontal;
@@ -118,7 +119,7 @@
         final Resources res = mContext.getResources();
         mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
         updateColumns();
-        mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+        mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId);
         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
@@ -235,7 +236,7 @@
         layoutTileRecords(mRecords.size());
     }
 
-    private int getRowTop(int row) {
+    protected int getRowTop(int row) {
         return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
index f8c0dd4..7977b49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
@@ -14,7 +14,7 @@
 class CustomizeTileViewHorizontal(
     context: Context,
     icon: QSIconView
-) : QSTileViewHorizontal(context, icon),
+) : QSTileViewHorizontal(context, icon, collapsed = false),
     TileAdapter.CustomizeView {
 
     private var showAppLabel = false
@@ -27,6 +27,7 @@
 
     override fun handleStateChanged(state: QSTile.State) {
         super.handleStateChanged(state)
+        mShowRippleEffect = false
         mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 56d06eb..1699a34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -51,6 +51,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -95,6 +96,7 @@
             QSHost host,
             Looper backgroundLooper,
             Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -102,8 +104,8 @@
             String action,
             Context userContext
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
         mComponent = ComponentName.unflattenFromString(action);
         mTile = new Tile();
@@ -450,6 +452,7 @@
         final Lazy<QSHost> mQSHostLazy;
         final Looper mBackgroundLooper;
         final Handler mMainHandler;
+        private final FalsingManager mFalsingManager;
         final MetricsLogger mMetricsLogger;
         final StatusBarStateController mStatusBarStateController;
         final ActivityStarter mActivityStarter;
@@ -463,6 +466,7 @@
                 Lazy<QSHost> hostLazy,
                 @Background Looper backgroundLooper,
                 @Main Handler mainHandler,
+                FalsingManager falsingManager,
                 MetricsLogger metricsLogger,
                 StatusBarStateController statusBarStateController,
                 ActivityStarter activityStarter,
@@ -471,6 +475,7 @@
             mQSHostLazy = hostLazy;
             mBackgroundLooper = backgroundLooper;
             mMainHandler = mainHandler;
+            mFalsingManager = falsingManager;
             mMetricsLogger = metricsLogger;
             mStatusBarStateController = statusBarStateController;
             mActivityStarter = activityStarter;
@@ -496,6 +501,7 @@
                     mQSHostLazy.get(),
                     mBackgroundLooper,
                     mMainHandler,
+                    mFalsingManager,
                     mMetricsLogger,
                     mStatusBarStateController,
                     mActivityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9e5fe73..29b9e64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -248,10 +248,10 @@
     public QSTileView createTileView(QSTile tile, boolean collapsedView) {
         Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
         QSIconView icon = tile.createTileView(context);
-        if (collapsedView) {
+        if (mSideLabels) {
+            return new QSTileViewHorizontal(context, icon, collapsedView);
+        } else if (collapsedView) {
             return new QSTileBaseView(context, icon, collapsedView);
-        } else if (mSideLabels) {
-            return new QSTileViewHorizontal(context, icon);
         } else {
             return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
         }
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 33ca7d6..a45b1319 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -58,13 +58,13 @@
     private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
     protected final Handler mHandler = new H();
     private final int[] mLocInScreen = new int[2];
-    private final FrameLayout mIconFrame;
+    protected final FrameLayout mIconFrame;
     protected QSIconView mIcon;
     protected RippleDrawable mRipple;
     protected Drawable mTileBackground;
     private String mAccessibilityClass;
     private boolean mTileState;
-    private boolean mCollapsedView;
+    protected boolean mCollapsedView;
     protected boolean mShowRippleEffect = true;
     private float mStrokeWidthActive;
     private float mStrokeWidthInactive;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 5a81676..f9d1def 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -58,6 +58,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
@@ -103,6 +104,7 @@
     private final StatusBarStateController mStatusBarStateController;
     protected final ActivityStarter mActivityStarter;
     private final UiEventLogger mUiEventLogger;
+    private final FalsingManager mFalsingManager;
     private final QSLogger mQSLogger;
     private volatile int mReadyState;
 
@@ -159,6 +161,7 @@
             QSHost host,
             Looper backgroundLooper,
             Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -171,6 +174,7 @@
 
         mUiHandler = mainHandler;
         mHandler = new H(backgroundLooper);
+        mFalsingManager = falsingManager;
         mQSLogger = qsLogger;
         mMetricsLogger = metricsLogger;
         mStatusBarStateController = statusBarStateController;
@@ -608,7 +612,9 @@
                                 mContext, mEnforcedAdmin);
                         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
                     } else {
-                        handleClick();
+                        if (!mFalsingManager.isFalseTap(true, 0.1)) {
+                            handleClick();
+                        }
                     }
                 } else if (msg.what == SECONDARY_CLICK) {
                     name = "handleSecondaryClick";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index b59326a..c7ed89b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -26,6 +26,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.Utils;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
@@ -116,7 +118,8 @@
         }
     }
 
-    private boolean shouldLabelBeSingleLine() {
+    protected boolean shouldLabelBeSingleLine() {
+        if (mCollapsedView) return true;
         if (mLabel.getLineCount() > mMaxLabelLines) {
             return true;
         } else if (!TextUtils.isEmpty(mSecondLine.getText())
@@ -138,14 +141,14 @@
             } else {
                 labelColor = mColorLabelUnavailable;
             }
-            mLabel.setTextColor(labelColor);
+            changeLabelColor(labelColor);
             mState = state.state;
             mLabel.setText(state.label);
         }
         if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
             mSecondLine.setText(state.secondaryLabel);
-            mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
-                    : View.VISIBLE);
+            mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView
+                    ? View.GONE : View.VISIBLE);
         }
         boolean dualTarget = mDualTargetAllowed && state.dualTarget;
         handleExpand(dualTarget);
@@ -160,6 +163,10 @@
         mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
     }
 
+    protected void changeLabelColor(ColorStateList color) {
+        mLabel.setTextColor(color);
+    }
+
     protected void handleExpand(boolean dualTarget) {
         mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
         mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
@@ -178,4 +185,10 @@
     public TextView getAppLabel() {
         return mSecondLine;
     }
+
+    @Nullable
+    @Override
+    public View getLabelContainer() {
+        return mLabelContainer;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 328c2c3..32285cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -21,8 +21,7 @@
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.graphics.drawable.Drawable
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.RoundRectShape
+import android.graphics.drawable.RippleDrawable
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.view.Gravity
 import android.widget.LinearLayout
@@ -32,25 +31,31 @@
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
 
-// Placeholder
-private const val CORNER_RADIUS = 40f
-private val RADII = (1..8).map { CORNER_RADIUS }.toFloatArray()
-
 open class QSTileViewHorizontal(
     context: Context,
-    icon: QSIconView
-) : QSTileView(context, icon, false) {
+    icon: QSIconView,
+    collapsed: Boolean
+) : QSTileView(context, icon, collapsed) {
 
-    protected var backgroundDrawable: ShapeDrawable? = null
+    protected var colorBackgroundDrawable: Drawable? = null
     private var paintColor = Color.WHITE
     private var paintAnimator: ValueAnimator? = null
+    private var labelAnimator: ValueAnimator? = null
 
     init {
         orientation = HORIZONTAL
+        gravity = Gravity.CENTER_VERTICAL or Gravity.START
         mDualTargetAllowed = false
+        val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
+        setPadding(padding, paddingTop, padding, paddingBottom)
+
         mBg.setImageDrawable(null)
+        mIconFrame.removeAllViews()
+        removeView(mIconFrame)
+        val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+        addView(mIcon, 0, LayoutParams(iconSize, iconSize))
+
         mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
-        mMaxLabelLines = 3
     }
 
     override fun createLabel() {
@@ -61,65 +66,112 @@
                 removeRule(RelativeLayout.ALIGN_PARENT_TOP)
             }
         }
+        mLabelContainer.setPadding(0, 0, 0, 0)
+        (mLabelContainer.layoutParams as MarginLayoutParams).apply {
+            marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+        }
         mLabel.gravity = Gravity.START
         mLabel.textDirection = TEXT_DIRECTION_LOCALE
         mSecondLine.gravity = Gravity.START
         mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
-        val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
-        mLabelContainer.setPaddingRelative(0, padding, padding, padding)
+
         (mLabelContainer.layoutParams as LayoutParams).gravity =
             Gravity.CENTER_VERTICAL or Gravity.START
+        if (mCollapsedView) {
+            mSecondLine.visibility = GONE
+        }
+    }
+
+    override fun shouldLabelBeSingleLine(): Boolean {
+        return true
     }
 
     override fun updateRippleSize() {
     }
 
     override fun newTileBackground(): Drawable? {
-        backgroundDrawable = ShapeDrawable(RoundRectShape(RADII, null, null))
-        return backgroundDrawable
+        val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+        colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+        return ripple
     }
 
     override fun setClickable(clickable: Boolean) {
         super.setClickable(clickable)
-        background = mTileBackground
+        background = if (clickable && mShowRippleEffect) {
+            mTileBackground
+        } else {
+            colorBackgroundDrawable
+        }
     }
 
     override fun handleStateChanged(state: QSTile.State) {
         super.handleStateChanged(state)
-        mSecondLine.setTextColor(mLabel.textColors)
         mLabelContainer.background = null
 
         val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
         val newColor = getCircleColor(state.state)
         if (allowAnimations) {
-            animateToNewState(newColor)
+            animateBackground(newColor)
         } else {
             if (newColor != paintColor) {
-                clearAnimator()
-                backgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))
+                clearBackgroundAnimator()
+                colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
+                    paintColor = newColor
+                }
                 paintColor = newColor
             }
         }
     }
 
-    private fun animateToNewState(newColor: Int) {
-        if (newColor != paintColor) {
-            clearAnimator()
-            paintAnimator = ValueAnimator.ofArgb(paintColor, newColor)
+    private fun animateBackground(newBackgroundColor: Int) {
+        if (newBackgroundColor != paintColor) {
+            clearBackgroundAnimator()
+            paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
                 .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
                     addUpdateListener { animation: ValueAnimator ->
                         val c = animation.animatedValue as Int
-                        backgroundDrawable?.setTintList(ColorStateList.valueOf(c))
-                        paintColor = c
+                        colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also {
+                            paintColor = c
+                        }
                     }
                     start()
                 }
         }
     }
 
-    private fun clearAnimator() {
+    override fun changeLabelColor(color: ColorStateList) {
+        val allowAnimations = animationsEnabled()
+        val currentColor = mLabel.textColors.defaultColor
+        if (currentColor != color.defaultColor) {
+            clearLabelAnimator()
+            if (allowAnimations) {
+                labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor)
+                    .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+                        addUpdateListener {
+                            setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int))
+                        }
+                        start()
+                }
+            } else {
+                setLabelsColor(color)
+            }
+        }
+    }
+
+    private fun setLabelsColor(color: ColorStateList) {
+        mLabel.setTextColor(color)
+        if (!mCollapsedView) {
+            mSecondLine.setTextColor(color)
+        }
+    }
+
+    private fun clearBackgroundAnimator() {
         paintAnimator?.cancel()?.also { paintAnimator = null }
     }
 
+    private fun clearLabelAnimator() {
+        labelAnimator?.cancel()?.also { labelAnimator = null }
+    }
+
     override fun handleExpand(dualTarget: Boolean) {}
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index b848590..07abb90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -38,6 +38,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.GlobalSetting;
@@ -63,6 +64,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -70,8 +72,8 @@
             BroadcastDispatcher broadcastDispatcher,
             Lazy<ConnectivityManager> lazyConnectivityManager
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBroadcastDispatcher = broadcastDispatcher;
         mLazyConnectivityManager = lazyConnectivityManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index dede627..2945c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -15,6 +15,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -30,6 +31,7 @@
     host: QSHost,
     @Background backgroundLooper: Looper,
     @Main mainHandler: Handler,
+    falsingManager: FalsingManager,
     metricsLogger: MetricsLogger,
     statusBarStateController: StatusBarStateController,
     activityStarter: ActivityStarter,
@@ -41,6 +43,7 @@
     host,
     backgroundLooper,
     mainHandler,
+    falsingManager,
     metricsLogger,
     statusBarStateController,
     activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index bf3e4be..49d3ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -59,6 +60,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -66,8 +68,8 @@
             BatteryController batteryController,
             SecureSettings secureSettings
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBatteryController = batteryController;
         mBatteryController.observe(getLifecycle(), this);
         int currentUser = host.getUserContext().getUserId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 1424244..56df4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -42,6 +42,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -70,14 +71,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             BluetoothController bluetoothController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = bluetoothController;
         mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
         mController.observe(getLifecycle(), mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 70287cd..0d73a5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -21,7 +21,6 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
-import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
@@ -50,13 +50,15 @@
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
             MetricsLogger metricsLogger,
+            FalsingManager falsingManager,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             IndividualSensorPrivacyController sensorPrivacyController,
             KeyguardStateController keyguardStateController) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
+                keyguardStateController);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index f03ce2c..fa99eed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -81,6 +82,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -90,8 +92,8 @@
             NetworkController networkController,
             HotspotController hotspotController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = castController;
         mDetailAdapter = new CastDetailAdapter();
         mKeyguard = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 6a574d1..8cc6ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -45,6 +45,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -76,14 +77,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             NetworkController networkController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mDataController = mController.getMobileDataController();
         mDetailAdapter = new CellularDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a6d9604..ca7cf83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -60,6 +61,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -67,8 +69,8 @@
             UserTracker userTracker,
             SecureSettings secureSettings
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
 
         mSetting = new SecureSetting(secureSettings, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 85f1245..61376f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -49,14 +50,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             DataSaverController dataSaverController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mDataSaverController = dataSaverController;
         mDataSaverController.observe(getLifecycle(), this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 3b9f5dc..a74a50e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles
 
+import android.content.ComponentName
 import android.content.Intent
 import android.os.Handler
 import android.os.Looper
@@ -27,10 +28,12 @@
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
 import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsActivity
+import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -40,24 +43,24 @@
 import com.android.systemui.util.settings.GlobalSettings
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
-import javax.inject.Provider
 
 class DeviceControlsTile @Inject constructor(
     host: QSHost,
     @Background backgroundLooper: Looper,
     @Main mainHandler: Handler,
+    falsingManager: FalsingManager,
     metricsLogger: MetricsLogger,
     statusBarStateController: StatusBarStateController,
     activityStarter: ActivityStarter,
     qsLogger: QSLogger,
     private val controlsComponent: ControlsComponent,
     private val featureFlags: FeatureFlags,
-    private val dialogProvider: Provider<ControlsDialog>,
     globalSettings: GlobalSettings
 ) : QSTileImpl<QSTile.State>(
         host,
         backgroundLooper,
         mainHandler,
+        falsingManager,
         metricsLogger,
         statusBarStateController,
         activityStarter,
@@ -72,7 +75,6 @@
     private var hasControlsApps = AtomicBoolean(false)
     private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
 
-    private var controlsDialog: ControlsDialog? = null
     private val icon = ResourceIcon.get(R.drawable.ic_device_light)
 
     private val listingCallback = object : ControlsListingController.ControlsListingCallback {
@@ -102,27 +104,19 @@
     }
 
     override fun handleDestroy() {
-        dismissDialog()
         super.handleDestroy()
     }
 
-    private fun createDialog() {
-        if (controlsDialog?.isShowing != true) {
-            controlsDialog = dialogProvider.get()
-        }
-    }
-
-    private fun dismissDialog() {
-        controlsDialog?.dismiss()?.also {
-            controlsDialog = null
-        }
-    }
-
     override fun handleClick() {
         if (state.state == Tile.STATE_ACTIVE) {
             mUiHandler.post {
-                createDialog()
-                controlsDialog?.show(controlsComponent.getControlsUiController().get())
+                mHost.collapsePanels()
+                val i = Intent().apply {
+                    component = ComponentName(mContext, ControlsActivity::class.java)
+                    addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+                    putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
+                }
+                mContext.startActivity(i)
             }
         }
     }
@@ -133,9 +127,6 @@
         state.contentDescription = state.label
         state.icon = icon
         if (controlsComponent.isEnabled() && hasControlsApps.get()) {
-            if (controlsDialog == null) {
-                mUiHandler.post(this::createDialog)
-            }
             if (controlsComponent.getVisibility() == AVAILABLE) {
                 state.state = Tile.STATE_ACTIVE
                 state.secondaryLabel = ""
@@ -146,7 +137,6 @@
             state.stateDescription = state.secondaryLabel
         } else {
             state.state = Tile.STATE_UNAVAILABLE
-            dismissDialog()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 7ec2691..4b96cf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -54,6 +54,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -87,6 +88,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -94,8 +96,8 @@
             ZenModeController zenModeController,
             @Main SharedPreferences sharedPreferences
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = zenModeController;
         mSharedPreferences = sharedPreferences;
         mDetailAdapter = new DndDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index cd45082..31a98db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -51,14 +52,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             FlashlightController flashlightController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mFlashlightController = flashlightController;
         mFlashlightController.observe(getLifecycle(), this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index a45d94a..4e0f634 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -57,6 +58,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -64,8 +66,8 @@
             HotspotController hotspotController,
             DataSaverController dataSaverController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mHotspotController = hotspotController;
         mDataSaverController = dataSaverController;
         mHotspotController.observe(this, mCallbacks);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index e1a1fd2..1a17e61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -40,9 +40,9 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.Icon;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.AlphaControlledSignalTileView;
@@ -80,14 +80,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             NetworkController networkController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mDataController = mController.getMobileDataController();
         mController.observe(getLifecycle(), mSignalCallback);
@@ -415,7 +416,6 @@
                 }
             } else {
                 state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
-                state.label = r.getString(R.string.quick_settings_airplane_safe_label);
             }
         } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
@@ -479,9 +479,6 @@
             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
             state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
         } else {
-            if (cb.mAirplaneModeEnabled) {
-                state.label = r.getString(R.string.quick_settings_airplane_safe_label);
-            }
             state.icon = new SignalIcon(cb.mMobileSignalIconId);
             state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
                     getMobileDataContentName(cb));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index d502d06..830a1fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -55,6 +56,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -62,8 +64,8 @@
             LocationController locationController,
             KeyguardStateController keyguardStateController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = locationController;
         mKeyguard = keyguardStateController;
         mController.observe(this, mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index e9b712d..b8d8792 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -21,7 +21,6 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
-import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
@@ -50,13 +50,15 @@
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
             MetricsLogger metricsLogger,
+            FalsingManager falsingManager,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             IndividualSensorPrivacyController sensorPrivacyController,
             KeyguardStateController keyguardStateController) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
+                keyguardStateController);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 63e27796..6ac2f9ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -36,6 +36,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -60,14 +61,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             BroadcastDispatcher broadcastDispatcher
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBroadcastDispatcher = broadcastDispatcher;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index d8548de..5369086 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -78,6 +79,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -86,8 +88,8 @@
             ColorDisplayManager colorDisplayManager,
             NightDisplayListenerModule.Builder nightDisplayListenerBuilder
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mLocationController = locationController;
         mManager = colorDisplayManager;
         mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index aec7b9a..479be65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -57,13 +58,14 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mReduceBrightColorsController = reduceBrightColorsController;
         mReduceBrightColorsController.observe(getLifecycle(), this);
         mIsAvailable = isAvailable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index c46cc4f..173cc05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -31,6 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -52,14 +53,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             RotationLockController rotationLockController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = rotationLockController;
         mController.observe(this, mCallback);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 0f0a9a2..6845dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -55,6 +56,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -62,8 +64,8 @@
             RecordingController controller,
             KeyguardDismissUtil keyguardDismissUtil
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = controller;
         mController.observe(this, mCallback);
         mKeyguardDismissUtil = keyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 0c582bd..a492330 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -59,14 +60,15 @@
     protected SensorPrivacyToggleTile(QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             IndividualSensorPrivacyController sensorPrivacyController,
             KeyguardStateController keyguardStateController) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mSensorPrivacyController = sensorPrivacyController;
         mKeyguard = keyguardStateController;
         mSensorPrivacyController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 78975a47..0ef5bc8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -66,6 +67,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -74,8 +76,8 @@
             BatteryController batteryController,
             LocationController locationController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBatteryController = batteryController;
         mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
         mLocationController = locationController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index a6cddd3..2590f37 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -28,6 +28,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
@@ -51,6 +52,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -58,8 +60,8 @@
             UserSwitcherController userSwitcherController,
             UserInfoController userInfoController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mUserSwitcherController = userSwitcherController;
         mUserInfoController = userInfoController;
         mUserInfoController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 341e67c..dab68ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -37,6 +37,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
@@ -77,6 +78,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -84,8 +86,8 @@
             NetworkController networkController,
             AccessPointController accessPointController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mWifiController = accessPointController;
         mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 5235b6d..c88a002 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -50,14 +51,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             ManagedProfileController managedProfileController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mProfileController = managedProfileController;
         mProfileController.observe(getLifecycle(), this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a87bfd8..8951605 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -294,11 +294,6 @@
         }
 
         @Override
-        public void setBackButtonAlpha(float alpha, boolean animate) {
-            setNavBarButtonAlpha(alpha, animate);
-        }
-
-        @Override
         public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
             if (!verifyCaller("onAssistantProgress")) {
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
new file mode 100644
index 0000000..9b3e386
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
@@ -0,0 +1,12 @@
+# Scroll Capture (Long Screenshots)
+# Bug component: 801322
+#
+# Referenced by:
+#
+# core/java/src/android/view/OWNERS
+# core/java/src/com/android/internal/view/OWNERS
+# core/tests/coretests/src/android/view/OWNERS
+# core/tests/coretests/src/com/android/internal/view/OWNERS
+
+mrcasey@google.com
+mrenouf@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c4fa6df..0599039 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -141,6 +141,7 @@
     private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 57 << MSG_SHIFT;
     //TODO(b/169175022) Update name and when feature name is locked.
     private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE      = 58 << MSG_SHIFT;
+    private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -369,6 +370,11 @@
          * Handles a window manager shell logging command.
          */
         default void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {}
+
+        /**
+         * @see IStatusBar#setNavigationBarLumaSamplingEnabled(int, boolean)
+         */
+        default void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {}
     }
 
     public CommandQueue(Context context) {
@@ -1019,6 +1025,14 @@
     }
 
     @Override
+    public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED, displayId,
+                    enable ? 1 : 0).sendToTarget();
+        }
+    }
+
+    @Override
     public void passThroughShellCommand(String[] args, ParcelFileDescriptor pfd) {
         final FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
         final PrintWriter pw = new PrintWriter(fos);
@@ -1400,6 +1414,12 @@
                     }
                     args.recycle();
                     break;
+                case MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).setNavigationBarLumaSamplingEnabled(msg.arg1,
+                                msg.arg2 != 0);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index d08f973..85a1aed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -146,17 +146,19 @@
         private final ExpandableNotificationRow mSourceNotification;
         private final ExpandAnimationParameters mParams;
         private final Rect mWindowCrop = new Rect();
-        private final float mNotificationCornerRadius;
-        private float mCornerRadius;
         private boolean mIsFullScreenLaunch = true;
         private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
 
-        public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
-            mSourceNotification = sourceNofitication;
+        private final float mNotificationStartTopCornerRadius;
+        private final float mNotificationStartBottomCornerRadius;
+
+        AnimationRunner(ExpandableNotificationRow sourceNotification) {
+            mSourceNotification = sourceNotification;
             mParams = new ExpandAnimationParameters();
             mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
-            mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
-                    mSourceNotification.getCurrentBottomRoundness());
+            mNotificationStartTopCornerRadius = mSourceNotification.getCurrentBackgroundRadiusTop();
+            mNotificationStartBottomCornerRadius =
+                    mSourceNotification.getCurrentBackgroundRadiusBottom();
         }
 
         @Override
@@ -224,7 +226,10 @@
                                         + notificationHeight,
                                 primary.position.y + primary.sourceContainerBounds.bottom,
                                 progress);
-                        mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
+                        mParams.topCornerRadius = MathUtils.lerp(mNotificationStartTopCornerRadius,
+                                mWindowCornerRadius, progress);
+                        mParams.bottomCornerRadius = MathUtils.lerp(
+                                mNotificationStartBottomCornerRadius,
                                 mWindowCornerRadius, progress);
                         applyParamsToWindow(primary);
                         applyParamsToNotification(mParams);
@@ -309,12 +314,13 @@
             Matrix m = new Matrix();
             m.postTranslate(0, (float) (mParams.top - app.position.y));
             mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
+            float cornerRadius = Math.min(mParams.topCornerRadius, mParams.bottomCornerRadius);
             SurfaceParams params = new SurfaceParams.Builder(app.leash)
                     .withAlpha(1f)
                     .withMatrix(m)
                     .withWindowCrop(mWindowCrop)
                     .withLayer(app.prefixOrderIndex)
-                    .withCornerRadius(mCornerRadius)
+                    .withCornerRadius(cornerRadius)
                     .withVisibility(true)
                     .build();
             mSyncRtTransactionApplier.scheduleApply(params);
@@ -339,6 +345,8 @@
         int bottom;
         int startClipTopAmount;
         int parentStartClipTopAmount;
+        float topCornerRadius;
+        float bottomCornerRadius;
 
         public ExpandAnimationParameters() {
         }
@@ -389,6 +397,14 @@
         public float getStartTranslationZ() {
             return startTranslationZ;
         }
+
+        public float getTopCornerRadius() {
+            return topCornerRadius;
+        }
+
+        public float getBottomCornerRadius() {
+            return bottomCornerRadius;
+        }
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 31d052d..18e5ead 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -883,8 +883,8 @@
     }
 
     private void applyBackgroundRoundness(float topRadius, float bottomRadius) {
-        mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
-        mBackgroundNormal.setRoundness(topRadius, bottomRadius);
+        mBackgroundDimmed.setRadius(topRadius, bottomRadius);
+        mBackgroundNormal.setRadius(topRadius, bottomRadius);
     }
 
     @Override
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 cde9f38..046fbd5 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
@@ -346,6 +346,9 @@
     private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
             new SystemNotificationAsyncTask();
 
+    private float mTopRoundnessDuringExpandAnimation;
+    private float mBottomRoundnessDuringExpandAnimation;
+
     /**
      * Returns whether the given {@code statusBarNotification} is a system notification.
      * <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC
@@ -2009,6 +2012,24 @@
         return false;
     }
 
+    @Override
+    public float getCurrentTopRoundness() {
+        if (mExpandAnimationRunning) {
+            return mTopRoundnessDuringExpandAnimation;
+        }
+
+        return super.getCurrentTopRoundness();
+    }
+
+    @Override
+    public float getCurrentBottomRoundness() {
+        if (mExpandAnimationRunning) {
+            return mBottomRoundnessDuringExpandAnimation;
+        }
+
+        return super.getCurrentBottomRoundness();
+    }
+
     public void applyExpandAnimationParams(ExpandAnimationParameters params) {
         if (params == null) {
             return;
@@ -2024,17 +2045,22 @@
         int top = params.getTop();
         float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
         int startClipTopAmount = params.getStartClipTopAmount();
+        int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
         if (mNotificationParent != null) {
             float parentY = mNotificationParent.getTranslationY();
             top -= parentY;
             mNotificationParent.setTranslationZ(translationZ);
+
+            // When the expanding notification is below its parent, the parent must be clipped
+            // exactly how it was clipped before the animation. When the expanding notification is
+            // on or above its parent (top <= 0), then the parent must be clipped exactly 'top'
+            // pixels to show the expanding notification, while still taking the decreasing
+            // notification clipTopAmount into consideration, so 'top + clipTopAmount'.
             int parentStartClipTopAmount = params.getParentStartClipTopAmount();
-            if (startClipTopAmount != 0) {
-                int clipTopAmount = (int) MathUtils.lerp(parentStartClipTopAmount,
-                        parentStartClipTopAmount - startClipTopAmount,
-                        interpolation);
-                mNotificationParent.setClipTopAmount(clipTopAmount);
-            }
+            int parentClipTopAmount = Math.min(parentStartClipTopAmount,
+                    top + clipTopAmount);
+            mNotificationParent.setClipTopAmount(parentClipTopAmount);
+
             mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
             float clipBottom = Math.max(params.getBottom(),
                     parentY + mNotificationParent.getActualHeight()
@@ -2043,12 +2069,15 @@
             int minimumHeightForClipping = (int) (clipBottom - clipTop);
             mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
         } else if (startClipTopAmount != 0) {
-            int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
             setClipTopAmount(clipTopAmount);
         }
         setTranslationY(top);
         setActualHeight(params.getHeight());
 
+        mTopRoundnessDuringExpandAnimation = params.getTopCornerRadius() / mOutlineRadius;
+        mBottomRoundnessDuringExpandAnimation = params.getBottomCornerRadius() / mOutlineRadius;
+        invalidateOutline();
+
         mBackgroundNormal.setExpandAnimationParams(params);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 85f556f..3728388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -27,7 +27,6 @@
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -83,8 +82,8 @@
     private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
         public void getOutline(View view, Outline outline) {
-            if (!mCustomOutline && mCurrentTopRoundness == 0.0f
-                    && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners
+            if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
+                    && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners
                     && !mTopAmountRounded) {
                 int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
                 int left = Math.max(translation, 0);
@@ -135,10 +134,12 @@
                 ? mOutlineRadius : getCurrentBackgroundRadiusBottom();
         if (topRoundness + bottomRoundness > height) {
             float overShoot = topRoundness + bottomRoundness - height;
-            topRoundness -= overShoot * mCurrentTopRoundness
-                    / (mCurrentTopRoundness + mCurrentBottomRoundness);
-            bottomRoundness -= overShoot * mCurrentBottomRoundness
-                    / (mCurrentTopRoundness + mCurrentBottomRoundness);
+            float currentTopRoundness = getCurrentTopRoundness();
+            float currentBottomRoundness = getCurrentBottomRoundness();
+            topRoundness -= overShoot * currentTopRoundness
+                    / (currentTopRoundness + currentBottomRoundness);
+            bottomRoundness -= overShoot * currentBottomRoundness
+                    / (currentTopRoundness + currentBottomRoundness);
         }
         getRoundedRectPath(left, top, right, bottom, topRoundness, bottomRoundness, mTmpPath);
         return mTmpPath;
@@ -267,7 +268,7 @@
         if (mTopAmountRounded) {
             return mOutlineRadius;
         }
-        return mCurrentTopRoundness * mOutlineRadius;
+        return getCurrentTopRoundness() * mOutlineRadius;
     }
 
     public float getCurrentTopRoundness() {
@@ -278,8 +279,8 @@
         return mCurrentBottomRoundness;
     }
 
-    protected float getCurrentBackgroundRadiusBottom() {
-        return mCurrentBottomRoundness * mOutlineRadius;
+    public float getCurrentBackgroundRadiusBottom() {
+        return getCurrentBottomRoundness() * mOutlineRadius;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 62d596b..9588563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -224,10 +224,9 @@
     }
 
     /**
-     * Sets the current top and bottom roundness amounts for this background, between 0.0 (not
-     * rounded) and 1.0 (maximally rounded).
+     * Sets the current top and bottom radius for this background.
      */
-    public void setRoundness(float topRoundness, float bottomRoundness) {
+    public void setRadius(float topRoundness, float bottomRoundness) {
         if (topRoundness == mCornerRadii[0] && bottomRoundness == mCornerRadii[4]) {
             return;
         }
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 d3065aa..8c21e76 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
@@ -349,7 +349,9 @@
         invalidateOutline();
         selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
         mForceSelectNextLayout = false;
-        updateExpandButtons(mExpandable);
+        // TODO(b/182314698): move this to onMeasure.  This requires switching to getMeasuredHeight,
+        //  and also requires revisiting all of the logic called earlier in this method.
+        updateExpandButtonsDuringLayout(mExpandable, true /* duringLayout */);
     }
 
     @Override
@@ -1344,9 +1346,7 @@
         }
         ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
         View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
-        LinearLayout actionContainerLayout =
-                layout.findViewById(com.android.internal.R.id.actions_container_layout);
-        if (bubbleButton == null || actionContainer == null || actionContainerLayout == null) {
+        if (bubbleButton == null || actionContainer == null) {
             return;
         }
         boolean isPersonWithShortcut =
@@ -1589,6 +1589,10 @@
     }
 
     public void updateExpandButtons(boolean expandable) {
+        updateExpandButtonsDuringLayout(expandable, false /* duringLayout */);
+    }
+
+    private void updateExpandButtonsDuringLayout(boolean expandable, boolean duringLayout) {
         mExpandable = expandable;
         // if the expanded child has the same height as the collapsed one we hide it.
         if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
@@ -1602,14 +1606,15 @@
                 expandable = false;
             }
         }
+        boolean requestLayout = duringLayout && mIsContentExpandable != expandable;
         if (mExpandedChild != null) {
-            mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
+            mExpandedWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
         }
         if (mContractedChild != null) {
-            mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
+            mContractedWrapper.updateExpandability(expandable, mExpandClickListener, requestLayout);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener);
+            mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener, requestLayout);
         }
         mIsContentExpandable = expandable;
     }
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 fb0fdcc..383bb7e 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
@@ -147,8 +147,11 @@
     override fun setRemoteInputVisible(visible: Boolean) =
             conversationLayout.showHistoricMessages(visible)
 
-    override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) =
-            conversationLayout.updateExpandability(expandable, onClickListener)
+    override fun updateExpandability(
+        expandable: Boolean,
+        onClickListener: View.OnClickListener,
+        requestLayout: Boolean
+    ) = conversationLayout.updateExpandability(expandable, onClickListener)
 
     override fun disallowSingleClick(x: Float, y: Float): Boolean {
         val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
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 bdafd23..5a55545 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
@@ -261,7 +261,8 @@
     }
 
     @Override
-    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener,
+            boolean requestLayout) {
         mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
         mExpandButton.setOnClickListener(expandable ? onClickListener : null);
         if (mAltExpandTarget != null) {
@@ -273,6 +274,13 @@
         if (mNotificationHeader != null) {
             mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
         }
+        // Unfortunately, the NotificationContentView has to layout its children in order to
+        // determine their heights, and that affects the button visibility.  If that happens
+        // (thankfully it is rare) then we need to request layout of the expand button's parent
+        // in order to ensure it gets laid out correctly.
+        if (requestLayout) {
+            mExpandButton.getParent().requestLayout();
+        }
     }
 
     @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 9ced12d..3a7b461 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
@@ -291,8 +291,10 @@
      *
      * @param expandable should this view be expandable
      * @param onClickListener the listener to invoke when the expand affordance is clicked on
+     * @param requestLayout the expandability changed during onLayout, so a requestLayout required
      */
-    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener,
+            boolean requestLayout) {}
 
     /** Set the expanded state on the view wrapper */
     public void setExpanded(boolean expanded) {}
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 d8ee102..1d30736 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
@@ -34,7 +34,6 @@
 import android.widget.TextView;
 
 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;
@@ -325,7 +324,7 @@
         StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
         final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
                 notification.getNotification());
-        RemoteViews header = builder.makeNotificationHeader();
+        RemoteViews header = builder.makeNotificationGroupHeader();
         if (mNotificationHeader == null) {
             mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
             mNotificationHeader.findViewById(com.android.internal.R.id.expand_button)
@@ -338,6 +337,7 @@
         } else {
             header.reapply(getContext(), mNotificationHeader);
         }
+        mNotificationHeaderWrapper.setExpanded(mChildrenExpanded);
         mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
         if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
             NotificationHeaderViewWrapper headerWrapper =
@@ -638,10 +638,6 @@
             childState.location = parentState.location;
             childState.inShelf = parentState.inShelf;
             yPosition += intrinsicHeight;
-            if (child.isExpandAnimationRunning()) {
-                launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
-            }
-
         }
         if (mOverflowNumber != null) {
             ExpandableNotificationRow overflowView = mAttachedChildren.get(Math.min(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 6c8cdf6..b06f7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -318,9 +318,6 @@
                     (child == null || row != null && nextBucket != row.entry.bucket)
             if (isSectionBoundary && showHeaders) {
                 when (nextBucket) {
-                    BUCKET_HEADS_UP -> incomingState?.targetPosition = i + 1
-                    BUCKET_PEOPLE -> peopleState?.targetPosition = i + 1
-                    BUCKET_ALERTING -> alertingState?.targetPosition = i + 1
                     BUCKET_SILENT -> gentleState?.targetPosition = i + 1
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 5d2203b..d7a98bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -28,14 +28,12 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.FooterView;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -156,7 +154,7 @@
     private void updateClipping(StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
         float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
-                + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
+                + ambientState.getStackTranslation()
                 : 0;
         float clipStart = 0;
         int childCount = algorithmState.visibleChildren.size();
@@ -329,9 +327,6 @@
         childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
         float inset = ambientState.getTopPadding() + ambientState.getStackTranslation()
                 + ambientState.getSectionPadding();
-        if (i <= algorithmState.getIndexOfExpandingNotification()) {
-            inset += ambientState.getExpandAnimationTopChange();
-        }
         if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
             // Even if we're not scrolled away we're in view and we're also not in the
             // shelf. We can relax the constraints and let us scroll off the top!
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index e6731e6..c60bbc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -166,6 +166,7 @@
 
         private int mGradientAlpha;
         private int mColor;
+        private float mOverrideAlpha = 1f;
         private PorterDuffColorFilter mTintFilter;
         private Paint mPaint = new Paint();
 
@@ -195,6 +196,23 @@
             mFrame = frame;
         }
 
+        public void setOverrideAlpha(float overrideAlpha) {
+            mOverrideAlpha = overrideAlpha;
+            invalidateSelf();
+        }
+
+        public float getOverrideAlpha() {
+            return mOverrideAlpha;
+        }
+
+        public int getColor() {
+            return mColor;
+        }
+
+        public Rect getFrame() {
+            return mFrame;
+        }
+
         @Override
         public void setAlpha(int alpha) {
             // noop
@@ -296,11 +314,13 @@
                 mGradient.setAlpha(mGradientAlpha);
                 mGradient.draw(canvas);
             }
+
             if (Color.alpha(mColor) > 0) {
                 mPaint.setColor(mColor);
                 if (mTintFilter != null) {
                     mPaint.setColorFilter(mTintFilter);
                 }
+                mPaint.setAlpha((int) (Color.alpha(mColor) * mOverrideAlpha));
                 if (mFrame != null) {
                     canvas.drawRect(mFrame, mPaint);
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 85d8df8..c23f1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -148,6 +148,11 @@
         return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
     }
 
+    public int getQuickPickupAodDuration() {
+        return getInt("doze.gesture.quickpickup.duration",
+                R.integer.doze_quick_pickup_aod_duration);
+    }
+
     /**
      * For how long a wallpaper can be visible in AoD before it fades aways.
      * @return duration in millis.
@@ -175,6 +180,10 @@
         return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
     }
 
+    public boolean isQuickPickupEnabled() {
+        return mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
+    }
+
     /**
      * Some screens need to be completely black before changing the display power mode,
      * unexpected behavior might happen if this parameter isn't respected.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 89b97ae..6b144c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
 import android.util.Property;
+import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.animation.Interpolator;
 
@@ -35,6 +36,7 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.Utils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
@@ -168,6 +170,7 @@
     private Rect mIsolatedIconLocation;
     private int[] mAbsolutePosition = new int[2];
     private View mIsolatedIconForAnimation;
+    private int mThemedTextColorPrimary;
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -179,6 +182,10 @@
         mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
         mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
         mStaticDotDiameter = 2 * mStaticDotRadius;
+        final Context themedContext = new ContextThemeWrapper(getContext(),
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        mThemedTextColorPrimary = Utils.getColorAttr(themedContext,
+                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
     }
 
     @Override
@@ -806,7 +813,8 @@
                     }
                 }
                 icon.setVisibleState(visibleState, animationsAllowed);
-                icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
+                icon.setIconColor(mThemedTextColorPrimary,
+                        needsCannedAnimation && animationsAllowed);
                 if (animate) {
                     animateTo(icon, animationProperties);
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 19b9895..ca6e53d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -439,7 +439,6 @@
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mUserSetupComplete;
     private int mQsNotificationTopPadding;
-    private float mExpandOffset;
     private boolean mHideIconsDuringNotificationLaunch = true;
     private int mStackScrollerMeasuringPass;
     private ArrayList<Consumer<ExpandableNotificationRow>>
@@ -576,8 +575,8 @@
             FeatureFlags featureFlags) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
-                latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
-                ambientState);
+                statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
+                statusBarTouchableRegionManager, ambientState);
         mView = view;
         mMetricsLogger = metricsLogger;
         mActivityManager = activityManager;
@@ -1349,7 +1348,6 @@
         super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
     }
 
-
     private boolean onQsIntercept(MotionEvent event) {
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
@@ -2444,8 +2442,7 @@
             }
             startHeight = -mQs.getQsMinExpansionHeight();
         }
-        float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
-                + mExpandOffset;
+        float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount));
         return Math.min(0, translation);
     }
 
@@ -3185,16 +3182,16 @@
     }
 
     public void applyExpandAnimationParams(ExpandAnimationParameters params) {
-        mExpandOffset = params != null ? params.getTopChange() : 0;
-        updateQsExpansion();
-        if (params != null) {
-            boolean hideIcons = params.getProgress(
-                    ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
-            if (hideIcons != mHideIconsDuringNotificationLaunch) {
-                mHideIconsDuringNotificationLaunch = hideIcons;
-                if (!hideIcons) {
-                    mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
-                }
+        if (params == null) {
+            return;
+        }
+
+        boolean hideIcons = params.getProgress(
+                ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+        if (hideIcons != mHideIconsDuringNotificationLaunch) {
+            mHideIconsDuringNotificationLaunch = hideIcons;
+            if (!hideIcons) {
+                mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
index 50c8e2e..66df936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
@@ -64,7 +64,7 @@
                 mTrackTouch = event.getY() <= maxTouchableHeight;
                 break;
             case MotionEvent.ACTION_MOVE:
-                if (mTrackTouch && mFalsingManager.isFalseTap(false)) {
+                if (mTrackTouch && mFalsingManager.isFalseTap(false, 0)) {
                     makeInactive();
                     mTrackTouch = false;
                 }
@@ -78,10 +78,10 @@
 
                 // 1) See if we have confidence that we can activate after a single tap.
                 // 2) Else, see if it looks like a tap at all and check for a double-tap.
-                if (!mFalsingManager.isFalseTap(true)) {
+                if (!mFalsingManager.isFalseTap(true, 0)) {
                     makeInactive();
                     return mDoubleTapListener.onDoubleTap();
-                } else if (!mFalsingManager.isFalseTap(false)) {
+                } else if (!mFalsingManager.isFalseTap(false, 0)) {
                     if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
                         return true;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 0c9ed66..1cb0be0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -89,30 +89,22 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        // Invert the order of the scroll view and user switcher such that the notifications receive
-        // touches first but the panel gets drawn above.
         mDrawingOrderedChildren.clear();
         mLayoutDrawingOrder.clear();
         if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
             mDrawingOrderedChildren.add(mKeyguardStatusBar);
             mLayoutDrawingOrder.add(mKeyguardStatusBar);
         }
-        if (mStackScroller.getVisibility() == View.VISIBLE) {
-            mDrawingOrderedChildren.add(mStackScroller);
-            mLayoutDrawingOrder.add(mStackScroller);
-        }
         if (mQsFrame.getVisibility() == View.VISIBLE) {
             mDrawingOrderedChildren.add(mQsFrame);
             mLayoutDrawingOrder.add(mQsFrame);
         }
-
-        if (mHasViewsAboveShelf) {
-            // StackScroller needs to be on top
-            mDrawingOrderedChildren.remove(mStackScroller);
+        if (mStackScroller.getVisibility() == View.VISIBLE) {
             mDrawingOrderedChildren.add(mStackScroller);
+            mLayoutDrawingOrder.add(mStackScroller);
         }
 
-        // Let's now find the order that the view has when drawing regulary by sorting
+        // Let's now find the order that the view has when drawing regularly by sorting
         mLayoutDrawingOrder.sort(mIndexComparator);
         super.dispatchDraw(canvas);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index b6ed3e5..3ac6937 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -167,6 +167,7 @@
     private boolean mIgnoreXTouchSlop;
     private boolean mExpandLatencyTracking;
     private final PanelView mView;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     protected final Resources mResources;
     protected final KeyguardStateController mKeyguardStateController;
     protected final SysuiStatusBarStateController mStatusBarStateController;
@@ -235,12 +236,14 @@
             FalsingManager falsingManager, DozeLog dozeLog,
             KeyguardStateController keyguardStateController,
             SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             LatencyTracker latencyTracker,
             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             AmbientState ambientState) {
         mAmbientState = ambientState;
         mView = view;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -1391,8 +1394,13 @@
 
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    addMovement(event);
-                    endMotionEvent(event, x, y, false /* forceCancel */);
+                    if (mStatusBarKeyguardViewManager.isBouncerShowing()
+                            && mFalsingManager.isFalseTap(true, 0.5)) {
+                        endMotionEvent(event, x, y, true /* forceCancel */);
+                    } else {
+                        addMovement(event);
+                        endMotionEvent(event, x, y, false /* forceCancel */);
+                    }
                     InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
                     if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                         monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d537241..f1405de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -675,8 +675,7 @@
 
         mIconController.setIconVisibility(mSlotCamera, showCamera);
         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        if (mPrivacyItemController.getAllIndicatorsAvailable()
-                || mPrivacyItemController.getLocationAvailable()) {
+        if (mPrivacyItemController.getLocationAvailable()) {
             mIconController.setIconVisibility(mSlotLocation, showLocation);
         }
         mPrivacyLogger.logStatusBarIconsVisible(showCamera, showMicrophone,  showLocation);
@@ -684,8 +683,7 @@
 
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!mPrivacyItemController.getAllIndicatorsAvailable()
-                && !mPrivacyItemController.getLocationAvailable()) {
+        if (!mPrivacyItemController.getLocationAvailable()) {
             updateLocationFromController();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 48c97a2..9b8b716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -140,11 +140,13 @@
         @Override
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
+            final boolean quickPickupEnabled = mDozeParameters.isQuickPickupEnabled();
             final boolean isDocked = mDockManager.isDocked();
             mBlankScreen = mDisplayRequiresBlanking;
 
             mFrontTint = Color.BLACK;
-            mFrontAlpha = (alwaysOnEnabled || isDocked) ? mAodFrontScrimAlpha : 1f;
+            mFrontAlpha = (alwaysOnEnabled || isDocked || quickPickupEnabled)
+                    ? mAodFrontScrimAlpha : 1f;
 
             mBehindTint = Color.BLACK;
             mBehindAlpha = ScrimController.TRANSPARENT;
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 0807f8a..117921d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -95,6 +95,7 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -4071,7 +4072,9 @@
     private @Nullable Intent getEmergencyActionIntent() {
         Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
         PackageManager pm = mContext.getPackageManager();
-        ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
+        List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
+                PackageManager.MATCH_SYSTEM_ONLY);
+        ResolveInfo resolveInfo = getTopEmergencySosInfo(emergencyActivities);
         if (resolveInfo == null) {
             Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
             return null;
@@ -4082,6 +4085,34 @@
         return emergencyIntent;
     }
 
+    /**
+     * Select and return the "best" ResolveInfo for Emergency SOS Activity.
+     */
+    private @Nullable ResolveInfo getTopEmergencySosInfo(List<ResolveInfo> emergencyActivities) {
+        // No matched activity.
+        if (emergencyActivities == null || emergencyActivities.isEmpty()) {
+            return null;
+        }
+
+        // Of multiple matched Activities, give preference to the pre-set package name.
+        String preferredAppPackageName =
+                mContext.getString(R.string.config_preferredEmergencySosPackage);
+
+        // If there is no preferred app, then return first match.
+        if (TextUtils.isEmpty(preferredAppPackageName)) {
+            return emergencyActivities.get(0);
+        }
+
+        for (ResolveInfo emergencyInfo: emergencyActivities) {
+            // If activity is from the preferred app, use it.
+            if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) {
+                return emergencyInfo;
+            }
+        }
+        // No matching activity: return first match
+        return emergencyActivities.get(0);
+    }
+
     boolean isCameraAllowedByAdmin() {
         if (mDevicePolicyManager.getCameraDisabled(null,
                 mLockscreenUserManager.getCurrentUserId())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5083e33..01ada0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -76,7 +76,7 @@
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
  * which is in turn, reported to this class by the current
- * {@link com.android.keyguard.KeyguardViewBase}.
+ * {@link com.android.keyguard.KeyguardViewController}.
  */
 @SysUISingleton
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 34673f2..801ac96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -356,9 +356,6 @@
         if (isActivityIntent || canBubble) {
             mAssistManagerLazy.get().hideAssist();
         }
-        if (shouldCollapse()) {
-            collapseOnMainThread();
-        }
 
         NotificationVisibility.NotificationLocation location =
                 NotificationLogger.getNotificationLocation(entry);
@@ -408,6 +405,12 @@
             mMainThreadHandler.post(
                     () -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
         }
+
+        // expandStackAndSelectBubble won't affect shouldCollapse, so we can collapse directly even
+        // if we are not on the main thread.
+        if (shouldCollapse()) {
+            collapseOnMainThread();
+        }
     }
 
     private void startNotificationIntent(
@@ -438,6 +441,9 @@
                     null, null, options);
             mMainThreadHandler.post(() -> {
                 mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
+                if (shouldCollapse()) {
+                    collapseOnMainThread();
+                }
             });
         } catch (RemoteException | PendingIntent.CanceledException e) {
             // the stack trace isn't very helpful here.
@@ -465,11 +471,11 @@
                     mActivityLaunchAnimator.setLaunchResult(launchResult,
                             true /* isActivityIntent */);
                     removeHUN(row);
+                    if (shouldCollapse()) {
+                        mCommandQueue.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                                true /* force */);
+                    }
                 });
-                if (shouldCollapse()) {
-                    mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
-                }
             });
             return true;
         }, null, false /* afterKeyguardGone */);
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 59c1138..4615877 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardConstants;
@@ -143,6 +144,16 @@
             openQsUserPanel();
         });
 
+        mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfo.ACTION_CLICK,
+                        mContext.getString(
+                                R.string.accessibility_quick_settings_choose_user_action)));
+            }
+        });
+
         updateView(true /* forceUpdate */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index ba58ed2..5cd3e57 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -52,6 +52,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -408,14 +409,15 @@
                 QSHost host,
                 @Background Looper backgroundLooper,
                 @Main Handler mainHandler,
+                FalsingManager falsingManager,
                 MetricsLogger metricsLogger,
                 StatusBarStateController statusBarStateController,
                 ActivityStarter activityStarter,
                 QSLogger qsLogger,
                 GarbageMonitor monitor
         ) {
-            super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                    activityStarter, qsLogger);
+            super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                    statusBarStateController, activityStarter, qsLogger);
             gm = monitor;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index a123269..3f4ec85 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -480,8 +480,8 @@
     @WMSingleton
     @Provides
     static StartingWindowController provideStartingWindowController(Context context,
-            @ShellSplashscreenThread ShellExecutor executor) {
-        return new StartingWindowController(context, executor);
+            @ShellSplashscreenThread ShellExecutor executor, TransactionPool pool) {
+        return new StartingWindowController(context, executor, pool);
     }
 
     //
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index c2ade81..0cf343c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -36,6 +36,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.classifier.FalsingCollectorFake;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,6 +71,7 @@
     private KeyguardMessageAreaController mKeyguardMessageAreaController;
     @Mock
     private LatencyTracker mLatencyTracker;
+    private final FalsingCollector mFalsingCollector = new FalsingCollectorFake();
 
     private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
 
@@ -84,7 +87,7 @@
                 .thenReturn(mKeyguardMessageArea);
         mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
-                mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
+                mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector) {
             @Override
             void resetState() {
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index c69ec1a..fc93ded 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,6 +24,8 @@
 import com.android.internal.widget.LockPatternView
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -48,6 +50,7 @@
     private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
     @Mock
     private lateinit var mLatencyTracker: LatencyTracker
+    private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
     @Mock
     private lateinit
     var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@@ -72,7 +75,7 @@
                 .thenReturn(mKeyguardMessageAreaController)
         mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
         mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
-                mLatencyTracker, mKeyguardMessageAreaControllerFactory)
+                mLatencyTracker, mFalsingCollector, mKeyguardMessageAreaControllerFactory)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 31cc7bb..33a0dcd0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.SingleTapClassifier;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,9 +70,12 @@
     private LiftToActivateListener mLiftToactivateListener;
     private FalsingCollector mFalsingCollector = new FalsingCollectorFake();
     @Mock
+    private SingleTapClassifier mSingleTapClassifier;
+    @Mock
     private View mDeleteButton;
     @Mock
     private View mOkButton;
+    private NumPadKey[] mButtons = new NumPadKey[]{};
 
     private KeyguardPinBasedInputViewController mKeyguardPinViewController;
 
@@ -83,6 +87,7 @@
         when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
         when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
         when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
+        when(mPinBasedInputView.getButtons()).thenReturn(mButtons);
         when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
                 .thenReturn(mKeyguardMessageArea);
         when(mPinBasedInputView.findViewById(R.id.delete_button))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index b03dc94..6272277 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -16,13 +16,19 @@
 
 package com.android.keyguard;
 
+import static android.view.WindowInsets.Type.ime;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowInsetsController;
@@ -33,7 +39,10 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -82,21 +91,41 @@
     @Mock
     private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
     @Mock
+    private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+    @Mock
+    private KeyguardMessageArea mKeyguardMessageArea;
+    @Mock
     private ConfigurationController mConfigurationController;
+    @Mock
+    private KeyguardViewController mKeyguardViewController;
+    private FalsingManager mFalsingManager = new FalsingManagerFake();
 
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+    private KeyguardPasswordViewController mKeyguardPasswordViewController;
+    private KeyguardPasswordView mKeyguardPasswordView;
 
     @Before
     public void setup() {
         when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
                 .thenReturn(mAdminSecondaryLockScreenController);
         when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+        mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
+        when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
+        when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
+                .thenReturn(mKeyguardMessageArea);
+        when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+        mKeyguardPasswordViewController = new KeyguardPasswordViewController(
+                (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
+                SecurityMode.Password, mLockPatternUtils, null,
+                mKeyguardMessageAreaControllerFactory, null, null, null, mock(Resources.class),
+                null);
 
         mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
                 mView,  mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
-                mConfigurationController).create(mSecurityCallback);
+                mConfigurationController, mKeyguardViewController, mFalsingManager)
+                .create(mSecurityCallback);
     }
 
     @Test
@@ -119,14 +148,13 @@
     public void startDisappearAnimation_animatesKeyboard() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
                 SecurityMode.Password);
-        when(mInputViewController.getSecurityMode()).thenReturn(
-                SecurityMode.Password);
         when(mKeyguardSecurityViewFlipperController.getSecurityView(
                 eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
-                .thenReturn(mInputViewController);
-        mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
 
         mKeyguardSecurityContainerController.startDisappearAnimation(null);
-        verify(mInputViewController).startDisappearAnimation(eq(null));
+        verify(mWindowInsetsController).controlWindowInsetsAnimation(
+                eq(ime()), anyLong(), any(), any(), any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 1783fa4..104318e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,9 +19,6 @@
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.systemBars;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -90,13 +87,6 @@
     }
 
     @Test
-    public void startDisappearAnimation_animatesKeyboard() {
-        mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
-        verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
-                any(), any());
-    }
-
-    @Test
     public void onMeasure_usesFullWidthWithoutOneHandedMode() {
         setUpKeyguard(
                 /* deviceConfigCanUseOneHandedKeyguard= */false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 0768618..d6f4958 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -246,20 +246,4 @@
         // THEN the illumination is hidden
         verify(mUdfpsView).stopIllumination();
     }
-
-    @Test
-    public void registersAndUnregistersViewForCallbacks() throws RemoteException {
-        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
-                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
-        verify(mStatusBar).addExpansionChangedListener(
-                mUdfpsController.mStatusBarExpansionListener);
-
-        mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
-        mFgExecutor.runAllReady();
-        verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener);
-        verify(mStatusBar).removeExpansionChangedListener(
-                mUdfpsController.mStatusBarExpansionListener);
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
new file mode 100644
index 0000000..480b335
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+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.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
+    // Dependencies
+    @Mock
+    private UdfpsKeyguardView mView;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private StatusBar mStatusBar;
+
+    private UdfpsKeyguardViewController mController;
+
+    // Capture listeners so that they can be used to send events
+    @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+    private StatusBarStateController.StateListener mParentListener;
+    private StatusBarStateController.StateListener mDozeListener;
+
+    @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
+    private StatusBar.ExpansionChangedListener mExpansionListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new UdfpsKeyguardViewController(
+                mView,
+                mStatusBarStateController,
+                mStatusBar);
+    }
+
+    @Test
+    public void testRegistersExpansionChangedListenerOnAttached() {
+        mController.onViewAttached();
+        captureExpansionListener();
+    }
+
+    @Test
+    public void testRegistersStatusBarStateListenersOnAttached() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+    }
+
+    @Test
+    public void testViewControllerQueriesSBStateOnAttached() {
+        mController.onViewAttached();
+        verify(mStatusBarStateController).getState();
+        verify(mStatusBarStateController).getDozeAmount();
+
+        final float dozeAmount = .88f;
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+        when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount);
+        captureStatusBarStateListeners();
+
+        mController.onViewAttached();
+        verify(mView).setPauseAuth(true);
+        verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount);
+    }
+
+    @Test
+    public void testListenersUnregisteredOnDetached() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        captureExpansionListener();
+        mController.onViewDetached();
+
+        verify(mStatusBarStateController).removeCallback(mParentListener);
+        verify(mStatusBarStateController).removeCallback(mDozeListener);
+        verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
+    }
+
+    @Test
+    public void testDozeEventsSentToView() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+
+        final float linear = .55f;
+        final float eased = .65f;
+        mDozeListener.onDozeAmountChanged(linear, eased);
+
+        verify(mView).onDozeAmountChanged(linear, eased);
+    }
+
+    private void captureStatusBarStateListeners() {
+        verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture());
+        List<StatusBarStateController.StateListener> stateListeners =
+                mStateListenerCaptor.getAllValues();
+        mParentListener = stateListeners.get(0);
+        mDozeListener = stateListeners.get(1);
+    }
+
+    private void captureExpansionListener() {
+        verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture());
+        mExpansionListener = mExpansionListenerCaptor.getValue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 5709ce30..b232850 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -21,10 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyCollection;
 import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -93,7 +90,7 @@
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier,
-                mHistoryTracker, mFakeExecutor, DOUBLE_TAP_TIMEOUT_MS, false);
+                mHistoryTracker, false);
 
 
         ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor =
@@ -161,25 +158,27 @@
     public void testIsFalseTap_BasicCheck() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
 
-        assertThat(mBrightLineFalsingManager.isFalseTap(false)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isTrue();
 
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
 
-        assertThat(mBrightLineFalsingManager.isFalseTap(false)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
     }
 
     @Test
     public void testIsFalseTap_RobustCheck_NoFaceAuth() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+        when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult);
+        when(mHistoryTracker.falseBelief()).thenReturn(1.0);
         mFalsingDataProvider.setJustUnlockedWithFace(false);
-        assertThat(mBrightLineFalsingManager.isFalseTap(true)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue();
     }
 
     @Test
     public void testIsFalseTap_RobustCheck_FaceAuth() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
         when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(true)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isFalse();
     }
 
     @Test
@@ -203,43 +202,29 @@
     @Test
     public void testHistory_singleTap() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(1000);
-
-        verify(mHistoryTracker, never()).addResults(any(), anyLong());
-
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runAllReady();
-
         verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
     }
 
     @Test
     public void testHistory_multipleSingleTaps() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(1000);
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(2000);
-
-        verify(mHistoryTracker, never()).addResults(any(), anyLong());
-
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runNextReady();
         verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
-        reset(mHistoryTracker);
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runNextReady();
         verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
     }
 
     @Test
     public void testHistory_doubleTap() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(1000);
         // Before checking for double tap, we may check for single-tap on the second gesture.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mBrightLineFalsingManager.isFalseDoubleTap();
         mGestureCompleteListener.onGestureComplete(2000);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 23ef865..dc79b88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,6 +57,8 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
+    private HistoryTracker mHistoryTracker;
+    @Mock
     private ProximitySensor mProximitySensor;
     @Mock
     private SysuiStatusBarStateController mStatusBarStateController;
@@ -67,7 +70,8 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
 
         mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
-                mKeyguardUpdateMonitor, mProximitySensor, mStatusBarStateController);
+                mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
+                mStatusBarStateController, new FakeSystemClock());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
index 01cce35..bb7545f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
@@ -48,14 +48,14 @@
 
     @Test
     public void testNoDataNoPenalty() {
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0);
+        assertThat(mHistoryTracker.falseBelief()).isEqualTo(0.5);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0);
     }
 
     @Test
     public void testOneResultFullConfidence() {
         addResult(true, 1);
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
     }
 
@@ -64,8 +64,8 @@
         addResult(true, 1);
         addResult(false, 1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0.5);
-        assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0.5);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(0.5);
+        assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(0.5);
     }
 
     @Test
@@ -73,20 +73,20 @@
         addResult(true, 1);
         addResult(true, 0);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0.75);
-        assertThat(mHistoryTracker.falseConfidence()).isEqualTo(.75);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
+        assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(.75);
     }
 
     @Test
     public void testDecay() {
         addResult(true, 1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
 
-        mSystemClock.advanceTime(1000);
+        mSystemClock.advanceTime(9999);
 
-        assertThat(mHistoryTracker.falsePenalty()).isWithin(0.01).of(0.1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.005).of(0.55);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
     }
 
@@ -96,25 +96,25 @@
         mSystemClock.advanceTime(1000);
         addResult(false, .5);
 
-        assertThat(mHistoryTracker.falsePenalty()).isWithin(0.01).of(0.17);
-        assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0.625);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.01).of(0.74);
+        assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(0.625);
     }
 
     @Test
     public void testCompleteDecay() {
         addResult(true, 1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
 
-        mSystemClock.advanceTime(2999);
+        mSystemClock.advanceTime(9999);
 
-        assertThat(mHistoryTracker.falsePenalty()).isGreaterThan(0);
+        assertThat(mHistoryTracker.falseBelief()).isGreaterThan(0);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
 
         mSystemClock.advanceTime(1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0);
+        assertThat(mHistoryTracker.falseBelief()).isEqualTo(0.5);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 9278570..17eb15b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -19,6 +19,7 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -63,6 +64,8 @@
     private lateinit var taskViewFactory: Optional<TaskViewFactory>
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private lateinit var cvh: ControlViewHolder
+    @Mock
+    private lateinit var metricsLogger: ControlsMetricsLogger
 
     companion object {
         fun <T> any(): T = Mockito.any<T>()
@@ -86,7 +89,8 @@
             globalActionsComponent,
             taskViewFactory,
             getFakeBroadcastDispatcher(),
-            lazyUiController
+            lazyUiController,
+            metricsLogger
         ))
 
         `when`(cvh.cws.ci.controlId).thenReturn(ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 9fd9b47..1aeb0d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -60,6 +60,7 @@
         when(config.tapSensorType()).thenReturn(null);
         when(config.longPressSensorType()).thenReturn(null);
         when(config.udfpsLongPressSensorType()).thenReturn(null);
+        when(config.quickPickupSensorType()).thenReturn(null);
 
         when(config.tapGestureEnabled(anyInt())).thenReturn(true);
         when(config.tapSensorAvailable()).thenReturn(true);
@@ -67,6 +68,7 @@
 
         when(config.dozePickupSensorAvailable()).thenReturn(false);
         when(config.wakeScreenGestureAvailable()).thenReturn(false);
+        when(config.quickPickupSensorEnabled(anyInt())).thenReturn(false);
 
         doneHolder[0] = true;
         return config;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 27187a8..1817fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -104,7 +104,7 @@
         mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
                 asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
                 mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
-                mAuthController);
+                mAuthController, mExecutor, mExecutor);
         mTriggers.setDozeMachine(mMachine);
         waitForSensorManager();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 1f9862c..3d4425c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -35,6 +35,7 @@
 
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -49,7 +50,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class SeekBarViewModelTest : SysuiTestCase() {
 
     private lateinit var viewModel: SeekBarViewModel
@@ -124,6 +125,7 @@
     }
 
     @Test
+    @Ignore
     fun updateDurationWithPlayback() {
         // GIVEN that the duration is contained within the metadata
         val duration = 12000L
@@ -146,6 +148,7 @@
     }
 
     @Test
+    @Ignore
     fun updateDurationWithoutPlayback() {
         // GIVEN that the duration is contained within the metadata
         val duration = 12000L
@@ -204,6 +207,7 @@
     }
 
     @Test
+    @Ignore
     fun updateDurationNoMetadata() {
         // GIVEN that the metadata is null
         whenever(mockController.getMetadata()).thenReturn(null)
@@ -235,6 +239,7 @@
     }
 
     @Test
+    @Ignore
     fun updateSeekAvailable() {
         // GIVEN that seek is included in actions
         val state = PlaybackState.Builder().run {
@@ -249,6 +254,7 @@
     }
 
     @Test
+    @Ignore
     fun updateSeekNotAvailable() {
         // GIVEN that seek is not included in actions
         val state = PlaybackState.Builder().run {
@@ -303,6 +309,7 @@
     }
 
     @Test
+    @Ignore
     fun onSeekProgressWithSeekStarting() {
         val pos = 42L
         with(viewModel) {
@@ -314,6 +321,7 @@
     }
 
     @Test
+    @Ignore
     fun onProgressChangedFromUser() {
         // WHEN user starts dragging the seek bar
         val pos = 42
@@ -614,6 +622,7 @@
     }
 
     @Test
+    @Ignore
     fun clearSeekBar() {
         // GIVEN that the duration is contained within the metadata
         val metadata = MediaMetadata.Builder().run {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 2b76f1c..22c553b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -23,6 +23,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
 
 import static org.junit.Assert.assertEquals;
@@ -31,6 +32,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -44,12 +46,16 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityManager;
@@ -79,6 +85,7 @@
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -101,6 +108,7 @@
     private OverviewProxyService mOverviewProxyService;
     private CommandQueue mCommandQueue;
     private SysUiState mMockSysUiState;
+    @Mock
     private Handler mHandler;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
@@ -124,12 +132,17 @@
         mDependency.injectMockDependency(NavigationBarController.class);
         mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
         TestableLooper.get(this).runWithLooper(() -> {
-            mHandler = new Handler();
             mNavigationBar = createNavBar(mContext);
             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
         });
     }
 
+    @After
+    public void tearDown() throws Exception {
+        DeviceConfig.resetToDefaults(
+                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
+    }
+
     private void setupSysuiDependency() {
         Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
                 new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -163,6 +176,32 @@
     }
 
     @Test
+    public void testHomeLongPressWithCustomDuration() throws Exception {
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
+                    .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
+                    .build());
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+
+        mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
+                /*downTime=*/SystemClock.uptimeMillis(),
+                /*eventTime=*/SystemClock.uptimeMillis(),
+                /*action=*/MotionEvent.ACTION_DOWN,
+                0, 0, 0
+        ));
+        verify(mHandler, times(1)).postDelayed(any(), eq(100L));
+
+        mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
+                /*downTime=*/SystemClock.uptimeMillis(),
+                /*eventTime=*/SystemClock.uptimeMillis(),
+                /*action=*/MotionEvent.ACTION_UP,
+                0, 0, 0
+        ));
+
+        verify(mHandler, times(1)).removeCallbacks(any());
+    }
+
+    @Test
     public void testRegisteredWithDispatcher() {
         mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
         verify(mBroadcastDispatcher).registerReceiverWithHandler(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 2a4b41c..3fed074 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -22,8 +22,9 @@
 import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
 import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
 
-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.getPeopleTileFromPersistentStorage;
+import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -76,6 +77,7 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.widget.PeopleTileKey;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -232,7 +234,7 @@
 
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         Bundle options = new Bundle();
-        options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
 
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
@@ -500,7 +502,7 @@
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromVisibleNotifications(mContext, tile,
-                        Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+                        Map.of(new PeopleTileKey(mNotificationEntry1), mNotificationEntry1));
 
         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
     }
@@ -515,7 +517,7 @@
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromVisibleNotifications(mContext, tile,
-                        Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+                        Map.of(new PeopleTileKey(mNotificationEntry1), mNotificationEntry1));
 
         assertThat(actual.getNotificationContent()).isEqualTo(null);
     }
@@ -818,6 +820,23 @@
         assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
     }
 
+    @Test
+    public void testGetPeopleTileFromPersistentStorageExistingConversation()
+            throws Exception {
+        when(mPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID_1)).thenReturn(
+                getConversationChannelWithoutTimestamp(SHORTCUT_ID_1));
+        PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_1, 0, PACKAGE_NAME);
+        PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager);
+        assertThat(tile.getId()).isEqualTo(key.getShortcutId());
+    }
+
+    @Test
+    public void testGetPeopleTileFromPersistentStorageNoConversation() {
+        PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_2, 0, PACKAGE_NAME);
+        PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager);
+        assertThat(tile).isNull();
+    }
+
     private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
             boolean importantConversation, long lastInteractionTimestamp) throws Exception {
         ConversationChannelWrapper convo = new ConversationChannelWrapper();
@@ -843,4 +862,13 @@
                 eq(shortcutId))).thenReturn(lastInteractionTimestamp);
         return convo;
     }
+
+    private ConversationChannel getConversationChannelWithoutTimestamp(String shortcutId)
+            throws Exception {
+        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+                "name").build();
+        ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+                0L, false);
+        return convo;
+    }
 }
\ No newline at end of file
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 f60fa09..aef75be 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
@@ -23,10 +23,11 @@
 import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
 import static android.app.people.ConversationStatus.ACTIVITY_GAME;
 
+import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
 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.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -104,13 +105,14 @@
     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
     private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
+    private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4;
     private static final String SHORTCUT_ID = "101";
     private static final String OTHER_SHORTCUT_ID = "102";
     private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
     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 PeopleTileKey KEY = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
     private static final Person PERSON = new Person.Builder()
             .setName("name")
             .setKey("abc")
@@ -172,10 +174,11 @@
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
 
+        clearStorage();
         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         Bundle options = new Bundle();
-        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
                 .thenReturn(options);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
@@ -395,7 +398,7 @@
                 .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
                         mBundleArgumentCaptor.capture());
         Bundle bundle = mBundleArgumentCaptor.getValue();
-        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
         assertThat(tile.getStatuses()).containsExactly(status);
         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
                 any());
@@ -439,7 +442,7 @@
                 .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
                         mBundleArgumentCaptor.capture());
         Bundle bundle = mBundleArgumentCaptor.getValue();
-        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
         assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
         assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
@@ -473,7 +476,8 @@
             throws Exception {
         addSecondWidgetForPersonTile();
 
-        PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        PeopleSpaceUtils.removeSharedPreferencesStorageForTile(
+                mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -510,7 +514,7 @@
                         mBundleArgumentCaptor.capture());
         Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
 
-        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
         assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
         assertThat(tile.getNotificationContent())
                 .isEqualTo(mContext.getString(R.string.missed_call));
@@ -536,7 +540,7 @@
                         mBundleArgumentCaptor.capture());
         Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
 
-        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
         assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
         assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
@@ -547,6 +551,7 @@
     public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         StatusBarNotification sbn = createNotification(
                 SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -560,11 +565,11 @@
         verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
                 mBundleArgumentCaptor.capture());
         Bundle bundle = mBundleArgumentCaptor.getValue();
-        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
         assertThat(tile.getNotificationKey()).isEqualTo(null);
         assertThat(tile.getNotificationContent()).isEqualTo(null);
         assertThat(tile.getNotificationDataUri()).isEqualTo(null);
-        verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
                 any());
     }
 
@@ -585,7 +590,7 @@
         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(
+        assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).containsExactly(
                 String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
         // Check listener & shortcut caching remain for other widget.
         verify(mPeopleManager, never()).unregisterConversationListener(any());
@@ -603,7 +608,7 @@
         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();
+        assertThat(sp.getStringSet(KEY.toString(), 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),
@@ -611,13 +616,96 @@
                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
     }
 
+    @Test
+    public void testUpdateWidgetsWithEmptyOptionsAddsPeopleTileToOptions() throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(new Bundle());
+
+        mManager.updateWidgets(widgetIdsArray);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        // If we had to fetch Tile from persistent storage, we want to make sure we write it to
+        // options.
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+        assertThat(tile.getId()).isEqualTo(SHORTCUT_ID);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testOnAppWidgetOptionsChangedNoWidgetAdded() {
+        Bundle newOptions = new Bundle();
+        newOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
+        mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
+
+
+        // Check that options is not modified
+        verify(mAppWidgetManager, never()).updateAppWidgetOptions(
+                eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any());
+        // Check listener is not added and shortcut is not cached.
+        verify(mPeopleManager, never()).registerConversationListener(any(), anyInt(), any(), any(),
+                any());
+        verify(mLauncherApps, never()).cacheShortcuts(any(), any(), any(), anyInt());
+        // Check no added storage.
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        assertThat(sp.getStringSet(KEY.toString(), new HashSet<>()))
+                .doesNotContain(SECOND_WIDGET_ID_WITH_SHORTCUT);
+        SharedPreferences widgetSp = mContext.getSharedPreferences(
+                String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                Context.MODE_PRIVATE);
+        assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
+        assertThat(widgetSp.getString(SHORTCUT_ID, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
+        assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+
+    }
+
+    @Test
+    public void testOnAppWidgetOptionsChangedWidgetAdded() {
+        Bundle newOptions = new Bundle();
+        newOptions.putString(PeopleSpaceUtils.SHORTCUT_ID, SHORTCUT_ID);
+        newOptions.putInt(USER_ID, 0);
+        newOptions.putString(PACKAGE_NAME, TEST_PACKAGE_A);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(newOptions);
+
+        mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
+
+        verify(mAppWidgetManager, times(1)).updateAppWidgetOptions(
+                eq(SECOND_WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        assertThat(bundle.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING))
+                .isEqualTo(EMPTY_STRING);
+        assertThat(bundle.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+        assertThat(bundle.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
+        verify(mLauncherApps, times(1)).cacheShortcuts(eq(TEST_PACKAGE_A),
+                eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
+                eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
+
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).contains(
+                String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
+        SharedPreferences widgetSp = mContext.getSharedPreferences(
+                String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                Context.MODE_PRIVATE);
+        assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(TEST_PACKAGE_A);
+        assertThat(widgetSp.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING))
+                .isEqualTo(SHORTCUT_ID);
+        assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(0);
+    }
+
     /**
      * 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);
+        options.putParcelable(OPTIONS_PEOPLE_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.
@@ -676,6 +764,27 @@
                 .build();
     }
 
+    private void clearStorage() {
+        SharedPreferences widgetSp1 = mContext.getSharedPreferences(
+                String.valueOf(WIDGET_ID_WITH_SHORTCUT),
+                Context.MODE_PRIVATE);
+        widgetSp1.edit().clear().commit();
+        SharedPreferences widgetSp2 = mContext.getSharedPreferences(
+                String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT),
+                Context.MODE_PRIVATE);
+        widgetSp2.edit().clear().commit();
+        SharedPreferences widgetSp3 = mContext.getSharedPreferences(
+                String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                Context.MODE_PRIVATE);
+        widgetSp3.edit().clear().commit();
+        SharedPreferences widgetSp4 = mContext.getSharedPreferences(
+                String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
+                Context.MODE_PRIVATE);
+        widgetSp4.edit().clear().commit();
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        sp.edit().clear().commit();
+    }
+
     private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
         SharedPreferences widgetSp = mContext.getSharedPreferences(
                 String.valueOf(widgetId),
@@ -689,7 +798,7 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
         SharedPreferences.Editor editor = sp.edit();
         editor.putString(String.valueOf(widgetId), shortcutId);
-        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+        String key = new PeopleTileKey(shortcutId, 0, packageName).toString();
         Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
         storedWidgetIds.add(String.valueOf(widgetId));
         editor.putStringSet(key, storedWidgetIds);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 072f7b8..791dd12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -395,9 +395,8 @@
         `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
                 listOf(usage_camera, usage_location, usage_microphone)
         )
-        `when`(privacyItemController.micCameraAvailable).thenReturn(false)
-        `when`(privacyItemController.locationAvailable).thenReturn(false)
-        `when`(privacyItemController.allIndicatorsAvailable).thenReturn(true)
+        `when`(privacyItemController.micCameraAvailable).thenReturn(true)
+        `when`(privacyItemController.locationAvailable).thenReturn(true)
 
         controller.showDialog(context)
         exhaustExecutors()
@@ -422,7 +421,6 @@
         )
         `when`(privacyItemController.micCameraAvailable).thenReturn(false)
         `when`(privacyItemController.locationAvailable).thenReturn(false)
-        `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false)
 
         controller.showDialog(context)
         exhaustExecutors()
@@ -525,7 +523,6 @@
 
         `when`(privacyItemController.locationAvailable).thenReturn(true)
         `when`(privacyItemController.micCameraAvailable).thenReturn(true)
-        `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false)
 
         `when`(userTracker.userProfiles).thenReturn(listOf(
                 UserInfo(USER_ID, "", 0),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 132bee0..f991e71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -37,7 +37,6 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -51,8 +50,6 @@
         fun <T> eq(value: T): T = Mockito.eq(value) ?: value
         fun <T> any(): T = Mockito.any<T>()
 
-        private const val ALL_INDICATORS =
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
         private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
     }
@@ -96,11 +93,6 @@
     }
 
     @Test
-    fun testNotListeningAllByDefault() {
-        assertFalse(privacyItemController.allIndicatorsAvailable)
-    }
-
-    @Test
     fun testMicCameraListeningByDefault() {
         assertTrue(privacyItemController.micCameraAvailable)
     }
@@ -111,10 +103,8 @@
         executor.runAllReady()
 
         verify(callback).onFlagMicCameraChanged(false)
-        verify(callback, never()).onFlagAllChanged(anyBoolean())
 
         assertFalse(privacyItemController.micCameraAvailable)
-        assertFalse(privacyItemController.allIndicatorsAvailable)
     }
 
     @Test
@@ -127,26 +117,15 @@
     }
 
     @Test
-    fun testAllChanged() {
-        changeAll(true)
-        executor.runAllReady()
-
-        verify(callback).onFlagAllChanged(true)
-        verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
-
-        assertTrue(privacyItemController.allIndicatorsAvailable)
-    }
-
-    @Test
     fun testBothChanged() {
         changeAll(true)
         changeMicCamera(false)
         executor.runAllReady()
 
-        verify(callback, atLeastOnce()).onFlagAllChanged(true)
+        verify(callback, atLeastOnce()).onFlagLocationChanged(true)
         verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
 
-        assertTrue(privacyItemController.allIndicatorsAvailable)
+        assertTrue(privacyItemController.locationAvailable)
         assertFalse(privacyItemController.micCameraAvailable)
     }
 
@@ -186,28 +165,6 @@
     }
 
     @Test
-    fun testSomeListening_stillListening() {
-        // Mic and camera are true by default
-        changeAll(true)
-        executor.runAllReady()
-        changeAll(false)
-        executor.runAllReady()
-
-        verify(appOpsController, never()).removeCallback(any(), any())
-    }
-
-    @Test
-    fun testAllDeleted_micCameraFalse_stopListening() {
-        changeMicCamera(false)
-        changeAll(true)
-        executor.runAllReady()
-        changeAll(null)
-        executor.runAllReady()
-
-        verify(appOpsController).removeCallback(any(), any())
-    }
-
-    @Test
     fun testMicDeleted_stillListening() {
         changeMicCamera(true)
         executor.runAllReady()
@@ -219,7 +176,10 @@
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
     private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
-    private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+    private fun changeAll(value: Boolean?) {
+        changeMicCamera(value)
+        changeLocation(value)
+    }
 
     private fun changeProperty(name: String, value: Boolean?) {
         deviceConfigProxy.setProperty(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 7ca468e..b87c7a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -43,7 +43,6 @@
 import org.junit.Assert.assertThat
 import org.junit.Assert.assertTrue
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -72,8 +71,8 @@
         val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
         const val TEST_PACKAGE_NAME = "test"
 
-        private const val ALL_INDICATORS =
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val LOCATION_INDICATOR =
+                SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
         fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
         fun <T> eq(value: T): T = Mockito.eq(value) ?: value
@@ -119,7 +118,8 @@
         deviceConfigProxy = DeviceConfigProxyFake()
 
         // Listen to everything by default
-        changeAll(true)
+        changeMicCamera(true)
+        changeLocation(true)
 
         `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0)))
 
@@ -259,9 +259,8 @@
     }
 
     @Test
-    @Ignore // TODO(b/168209929)
     fun testNotListeningWhenIndicatorsDisabled() {
-        changeAll(false)
+        changeLocation(false)
         changeMicCamera(false)
         privacyItemController.addCallback(callback)
         executor.runAllReady()
@@ -271,7 +270,7 @@
 
     @Test
     fun testNotSendingLocationWhenOnlyMicCamera() {
-        changeAll(false)
+        changeLocation(false)
         changeMicCamera(true)
         executor.runAllReady()
 
@@ -294,7 +293,7 @@
                 .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
 
         privacyItemController.addCallback(callback)
-        changeAll(false)
+        changeLocation(false)
         changeMicCamera(true)
         executor.runAllReady()
         reset(callback) // Clean callback
@@ -521,7 +520,7 @@
     }
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
-    private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+    private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value)
 
     private fun changeProperty(name: String, value: Boolean?) {
         deviceConfigProxy.setProperty(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index cfef5be..2ca8082 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSFactory;
@@ -360,6 +361,7 @@
                     host,
                     mLooper.getLooper(),
                     new Handler(mLooper.getLooper()),
+                    new FalsingManagerFake(),
                     mock(MetricsLogger.class),
                     mock(StatusBarStateController.class),
                     mock(ActivityStarter.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 97a8459..4948c2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -148,7 +148,7 @@
 
     @Test
     fun testIgnoredSlotsOnAttached_noIndicators() {
-        setPrivacyController(false, false, false)
+        setPrivacyController(micCamera = false, location = false)
 
         controller.init()
 
@@ -160,7 +160,7 @@
 
     @Test
     fun testIgnoredSlotsOnAttached_onlyMicCamera() {
-        setPrivacyController(false, true, false)
+        setPrivacyController(micCamera = true, location = false)
 
         controller.init()
 
@@ -177,7 +177,7 @@
 
     @Test
     fun testIgnoredSlotsOnAttached_onlyLocation() {
-        setPrivacyController(false, false, true)
+        setPrivacyController(micCamera = false, location = true)
 
         controller.init()
 
@@ -192,26 +192,7 @@
 
     @Test
     fun testIgnoredSlotsOnAttached_locationMicCamera() {
-        setPrivacyController(false, true, true)
-
-        controller.init()
-
-        val captor = argumentCaptor<List<String>>()
-        verify(iconContainer).setIgnoredSlots(capture(captor))
-
-        val cameraString = mContext.resources.getString(
-                com.android.internal.R.string.status_bar_camera)
-        val micString = mContext.resources.getString(
-                com.android.internal.R.string.status_bar_microphone)
-        val locationString = mContext.resources.getString(
-                com.android.internal.R.string.status_bar_location)
-
-        assertThat(captor.value).containsExactly(cameraString, micString, locationString)
-    }
-
-    @Test
-    fun testIgnoredSlotsOnAttached_all() {
-        setPrivacyController(true, false, false)
+        setPrivacyController(micCamera = true, location = true)
 
         controller.init()
 
@@ -248,8 +229,7 @@
         `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
     }
 
-    private fun setPrivacyController(all: Boolean, micCamera: Boolean, location: Boolean) {
-        `when`(privacyItemController.allIndicatorsAvailable).thenReturn(all)
+    private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
         `when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
         `when`(privacyItemController.locationAvailable).thenReturn(location)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 3aa40de..b1c3d1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -32,6 +32,7 @@
 import android.view.IWindowManager
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -103,6 +104,7 @@
                 { tileHost },
                 testableLooper.looper,
                 Handler(testableLooper.looper),
+                FalsingManagerFake(),
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 61a0d6c..937ab1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -26,6 +26,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -55,7 +57,9 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSEvent;
@@ -90,6 +94,7 @@
     private QSTileHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
+    private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
@@ -112,7 +117,7 @@
 
         Handler mainHandler = new Handler(mTestableLooper.getLooper());
 
-        mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler,
+        mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager,
                 mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
         mTile.setTileSpec(SPEC);
     }
@@ -144,6 +149,19 @@
     }
 
     @Test
+    public void testClick_falsing() {
+        mFalsingManager.setFalseRobustTap(true);
+        mTile.click();
+        mTestableLooper.processAllMessages();
+        assertThat(mTile.mClicked).isFalse();
+
+        mFalsingManager.setFalseRobustTap(false);
+        mTile.click();
+        mTestableLooper.processAllMessages();
+        assertThat(mTile.mClicked).isTrue();
+    }
+
+    @Test
     public void testSecondaryClick_Metrics() {
         mTile.secondaryClick();
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
@@ -360,17 +378,20 @@
     }
 
     private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
+        boolean mClicked;
+
         protected TileImpl(
                 QSHost host,
                 Looper backgroundLooper,
                 Handler mainHandler,
+                FalsingManager falsingManager,
                 MetricsLogger metricsLogger,
                 StatusBarStateController statusBarStateController,
                 ActivityStarter activityStarter,
                 QSLogger qsLogger
         ) {
-            super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                    activityStarter, qsLogger);
+            super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                    statusBarStateController, activityStarter, qsLogger);
             getState().state = Tile.STATE_ACTIVE;
         }
 
@@ -381,7 +402,7 @@
 
         @Override
         protected void handleClick() {
-
+            mClicked = true;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index a9d10e9..9674a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -10,6 +10,7 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -73,6 +74,7 @@
             qsHost,
             testableLooper.looper,
             Handler(testableLooper.looper),
+            FalsingManagerFake(),
             metricsLogger,
             statusBarStateController,
             activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index bcfc835..f17bd56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -79,6 +80,7 @@
                 qsHost,
                 testableLooper.looper,
                 Handler(testableLooper.looper),
+                FalsingManagerFake(),
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 1c29a81..7d39361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -101,6 +102,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index ccd9548..9fe5687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.qs.tiles
 
 import android.os.Handler
+import android.content.Context
+import android.content.Intent
 import android.provider.Settings
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
@@ -26,11 +28,11 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.ui.ControlsDialog
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -50,7 +52,9 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import java.util.Optional
@@ -80,8 +84,6 @@
     private lateinit var controlsController: ControlsController
     @Mock
     private lateinit var featureFlags: FeatureFlags
-    @Mock
-    private lateinit var controlsDialog: ControlsDialog
     private lateinit var globalSettings: GlobalSettings
     @Mock
     private lateinit var serviceInfo: ControlsServiceInfo
@@ -95,6 +97,7 @@
     private lateinit var tile: DeviceControlsTile
 
     private lateinit var secureSettings: SecureSettings
+    private lateinit var spiedContext: Context
     private var featureEnabled = true
 
     @Before
@@ -103,7 +106,9 @@
         testableLooper = TestableLooper.get(this)
         secureSettings = FakeSettings()
 
-        `when`(qsHost.context).thenReturn(mContext)
+        spiedContext = spy(mContext)
+        doNothing().`when`(spiedContext).startActivity(any(Intent::class.java))
+        `when`(qsHost.context).thenReturn(spiedContext)
         `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
         `when`(controlsController.available).thenReturn(true)
         `when`(controlsComponent.isEnabled()).thenReturn(true)
@@ -276,7 +281,7 @@
         tile.click()
         testableLooper.processAllMessages()
 
-        verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
+        verify(spiedContext, never()).startActivity(any(Intent::class.java))
     }
 
     @Test
@@ -293,7 +298,7 @@
         tile.click()
         testableLooper.processAllMessages()
 
-        verify(controlsDialog).show(controlsUiController)
+        verify(spiedContext).startActivity(any(Intent::class.java))
     }
 
     @Test
@@ -311,25 +316,7 @@
         tile.click()
         testableLooper.processAllMessages()
 
-        verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
-    }
-
-    @Test
-    fun testDialogDismissedOnDestroy() {
-        verify(controlsListingController).observe(
-                any(LifecycleOwner::class.java),
-                capture(listingCallbackCaptor)
-        )
-
-        listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
-        testableLooper.processAllMessages()
-
-        tile.click()
-        testableLooper.processAllMessages()
-
-        tile.destroy()
-        testableLooper.processAllMessages()
-        verify(controlsDialog).dismiss()
+        verify(spiedContext, never()).startActivity(any(Intent::class.java))
     }
 
     private fun createTile(): DeviceControlsTile {
@@ -337,13 +324,13 @@
                 qsHost,
                 testableLooper.looper,
                 Handler(testableLooper.looper),
+                FalsingManagerFake(),
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
                 qsLogger,
                 controlsComponent,
                 featureFlags,
-                { controlsDialog },
                 globalSettings
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index b37ac4a..99d028c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -84,6 +85,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 880c290..6032e51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -33,6 +33,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -82,6 +83,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 6b54791..2215433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -33,6 +33,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -81,6 +82,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 2917dfa..8ec03d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -476,4 +476,11 @@
         waitForIdleSync();
         verify(mCallbacks).requestWindowMagnificationConnection(true);
     }
+
+    @Test
+    public void testSetEnableNavigationBarLumaSampling() {
+        mCommandQueue.setNavigationBarLumaSamplingEnabled(1, true);
+        waitForIdleSync();
+        verify(mCallbacks).setNavigationBarLumaSamplingEnabled(eq(1), eq(true));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 8cd7103..c1d2ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -322,23 +322,7 @@
     }
 
     @Test
-    public void testPeopleFiltering_addHeadersFromShowingOnlyGentle() {
-        enablePeopleFiltering();
-
-        setStackState(
-                GENTLE_HEADER,
-                PERSON,
-                ALERTING,
-                GENTLE);
-        mSectionsManager.updateSectionBoundaries();
-
-        verify(mNssl).changeViewPosition(mSectionsManager.getSilentHeaderView(), 2);
-        verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
-        verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
-    }
-
-    @Test
-    public void testPeopleFiltering_addAllHeaders() {
+    public void testPeopleFiltering_onlyAddSilentHeader() {
         enablePeopleFiltering();
 
         setStackState(
@@ -348,26 +332,6 @@
         mSectionsManager.updateSectionBoundaries();
 
         verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 2);
-        verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
-        verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
-    }
-
-    @Test
-    public void testPeopleFiltering_moveAllHeaders() {
-        enablePeopleFiltering();
-
-        setStackState(
-                PEOPLE_HEADER,
-                ALERTING_HEADER,
-                GENTLE_HEADER,
-                PERSON,
-                ALERTING,
-                GENTLE);
-        mSectionsManager.updateSectionBoundaries();
-
-        verify(mNssl).changeViewPosition(mSectionsManager.getSilentHeaderView(), 4);
-        verify(mNssl).changeViewPosition(mSectionsManager.getAlertingHeaderView(), 2);
-        verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
     }
 
     @Test
@@ -385,9 +349,7 @@
         mSectionsManager.updateSectionBoundaries();
 
         verifyMockStack(
-                ChildType.INCOMING_HEADER,
                 ChildType.HEADS_UP,
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON,
                 ChildType.GENTLE_HEADER,
                 ChildType.GENTLE
@@ -408,10 +370,8 @@
         mSectionsManager.updateSectionBoundaries();
 
         verifyMockStack(
-                ChildType.INCOMING_HEADER,
                 ChildType.HEADS_UP,
                 ChildType.HEADS_UP,
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON
         );
     }
@@ -428,7 +388,6 @@
         mSectionsManager.updateSectionBoundaries();
 
         verifyMockStack(
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON,
                 ChildType.PERSON
         );
@@ -444,9 +403,7 @@
         );
         mSectionsManager.updateSectionBoundaries();
         verifyMockStack(
-                ChildType.INCOMING_HEADER,
                 ChildType.HEADS_UP,
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON
         );
     }
@@ -467,12 +424,9 @@
         mSectionsManager.updateSectionBoundaries();
 
         verifyMockStack(
-                ChildType.INCOMING_HEADER,
                 ChildType.HEADS_UP,
                 ChildType.FSN,
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON,
-                ChildType.ALERTING_HEADER,
                 ChildType.ALERTING,
                 ChildType.GENTLE_HEADER,
                 ChildType.GENTLE
@@ -517,7 +471,7 @@
     }
 
     @Test
-    public void testRemoveIncomingHeader() {
+    public void testRemoveNonSilentHeader() {
         enablePeopleFiltering();
         enableMediaControls();
 
@@ -539,9 +493,7 @@
 
         verifyMockStack(
                 ChildType.MEDIA_CONTROLS,
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON,
-                ChildType.ALERTING_HEADER,
                 ChildType.ALERTING,
                 ChildType.ALERTING,
                 ChildType.ALERTING,
@@ -569,13 +521,10 @@
         mSectionsManager.updateSectionBoundaries();
 
         verifyMockStack(
-                ChildType.INCOMING_HEADER,
                 ChildType.HEADS_UP,
                 ChildType.HEADS_UP,
                 ChildType.HEADS_UP,
-                ChildType.PEOPLE_HEADER,
                 ChildType.PERSON,
-                ChildType.ALERTING_HEADER,
                 ChildType.ALERTING
         );
     }
@@ -593,7 +542,6 @@
         mSectionsManager.updateSectionBoundaries();
 
         verifyMockStack(
-                ChildType.ALERTING_HEADER,
                 ChildType.PERSON,
                 ChildType.ALERTING,
                 ChildType.GENTLE_HEADER,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index bdde822..8b5ba38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -174,6 +174,7 @@
                         DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
                         DozeLog.PULSE_REASON_DOCKING,
                         DozeLog.REASON_SENSOR_WAKE_UP,
+                        DozeLog.REASON_SENSOR_QUICK_PICKUP,
                         DozeLog.REASON_SENSOR_TAP));
         HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
                 Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index a844d09..a60baa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -86,6 +87,7 @@
                 mRemoteInputQuickSettingsDisabler);
         mDependency.injectTestDependency(LightBarController.class,
                 mLightBarController);
+        mDependency.injectMockDependency(NotificationRemoteInputManager.class);
 
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index f8b9309..f0de811 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -360,7 +360,7 @@
             try {
                 mCallback.setProxyPort(port);
             } catch (RemoteException e) {
-                Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+                Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
             }
         }
         mPort = port;
@@ -371,7 +371,7 @@
             try {
                 callback.setProxyPort(mPort);
             } catch (RemoteException e) {
-                Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+                Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
             }
         }
         mCallback = callback;
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
index bdf478d..a8e2622 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
@@ -30,7 +30,7 @@
 
     private static ProxyServer server = null;
 
-    /** Keep these values up-to-date with PacProxyInstaller.java */
+    /** Keep these values up-to-date with PacProxyService.java */
     public static final String KEY_PROXY = "keyProxy";
     public static final String HOST = "localhost";
     public static final String EXCL_LIST = "";
diff --git a/services/api/Android.bp b/services/api/Android.bp
index e69de29..b8ca548 100644
--- a/services/api/Android.bp
+++ b/services/api/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "non-updatable-system-server-current.txt",
+    srcs: ["non-updatable-current.txt"],
+    visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+    name: "non-updatable-system-server-removed.txt",
+    srcs: ["non-updatable-removed.txt"],
+    visibility: ["//frameworks/base/api"],
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 4b56845..92af080 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -963,7 +963,8 @@
     private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
         return CollectionUtils.filter(
                 getAllAssociations(userId),
-                a -> Objects.equals(packageFilter, a.getPackageName()));
+                // Null filter == get all associations
+                a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
     }
 
     private Set<Association> getAllAssociations() {
@@ -983,8 +984,10 @@
             int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
         return CollectionUtils.filter(
                 getAllAssociations(userId),
-                a -> Objects.equals(packageFilter, a.getPackageName())
-                        && Objects.equals(addressFilter, a.getDeviceMacAddress()));
+                // Null filter == get all associations
+                a -> (packageFilter == null || Objects.equals(packageFilter, a.getPackageName()))
+                        && (addressFilter == null
+                                || Objects.equals(addressFilter, a.getDeviceMacAddress())));
     }
 
     private Set<Association> readAllAssociations(int userId) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8ccfad6..0fe874c 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -81,10 +81,19 @@
     out: ["services.core.protolog.json"],
 }
 
+genrule {
+    name: "statslog-art-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module art" +
+         " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource",
+    out: ["com/android/internal/art/ArtStatsLog.java"],
+}
+
 java_library_static {
     name: "services.core.unboosted",
     defaults: ["platform_service_defaults"],
     srcs: [
+        ":statslog-art-java-gen",
         ":services.core-sources",
         ":services.core.protologsrc",
         ":dumpstate_aidl",
@@ -98,7 +107,6 @@
         ":platform-compat-overrides",
         ":display-device-config",
         ":display-layout-config",
-        ":cec-config",
         ":device-state-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
@@ -119,7 +127,6 @@
     ],
 
     required: [
-        "cec_config.xml",
         "gps_debug.conf",
         "protolog.conf.json.gz",
     ],
@@ -185,11 +192,6 @@
 }
 
 prebuilt_etc {
-    name: "cec_config.xml",
-    src: "java/com/android/server/hdmi/cec_config.xml",
-}
-
-prebuilt_etc {
     name: "gps_debug.conf",
     src: "java/com/android/server/location/gnss/gps_debug.conf",
 }
@@ -223,6 +225,7 @@
         "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/ConnectivityResources.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/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index bb4bbd5..1e608f5 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -918,19 +918,7 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                if (!mUpdatesStopped) {
-                    copy(mLastHealthInfo, mHealthInfo);
-                }
-                mHealthInfo.chargerAcOnline = false;
-                mHealthInfo.chargerUsbOnline = false;
-                mHealthInfo.chargerWirelessOnline = false;
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    mUpdatesStopped = true;
-                    processValuesFromShellLocked(pw, opts);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
             } break;
             case "set": {
                 int opts = parseOptions(shell);
@@ -990,7 +978,8 @@
                         final long ident = Binder.clearCallingIdentity();
                         try {
                             mUpdatesStopped = true;
-                            processValuesFromShellLocked(pw, opts);
+                            processValuesLocked(
+                                    /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
                         } finally {
                             Binder.restoreCallingIdentity(ident);
                         }
@@ -1004,30 +993,12 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    if (mUpdatesStopped) {
-                        mUpdatesStopped = false;
-                        copy(mHealthInfo, mLastHealthInfo);
-                        processValuesFromShellLocked(pw, opts);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-                if (mBatteryInputSuspended) {
-                    PowerProperties.battery_input_suspended(false);
-                    mBatteryInputSuspended = false;
-                }
+                resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
             } break;
             case "suspend_input": {
-                if (!Build.IS_DEBUGGABLE) {
-                    throw new SecurityException(
-                            "battery suspend_input is only supported on debuggable builds");
-                }
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                PowerProperties.battery_input_suspended(true);
-                mBatteryInputSuspended = true;
+                suspendBatteryInput();
             } break;
             default:
                 return shell.handleDefaultCommands(cmd);
@@ -1035,9 +1006,59 @@
         return 0;
     }
 
-    private void processValuesFromShellLocked(PrintWriter pw, int opts) {
-        processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
-        if ((opts & OPTION_FORCE_UPDATE) != 0) {
+    private void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        if (!mUpdatesStopped) {
+            copy(mLastHealthInfo, mHealthInfo);
+        }
+        mHealthInfo.chargerAcOnline = online;
+        mUpdatesStopped = true;
+        Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+    }
+
+    private void setBatteryLevel(int level, boolean forceUpdate) {
+        if (!mUpdatesStopped) {
+            copy(mLastHealthInfo, mHealthInfo);
+        }
+        mHealthInfo.batteryLevel = level;
+        mUpdatesStopped = true;
+        Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+    }
+
+    private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
+        if (!mUpdatesStopped) {
+            copy(mLastHealthInfo, mHealthInfo);
+        }
+        mHealthInfo.chargerAcOnline = false;
+        mHealthInfo.chargerUsbOnline = false;
+        mHealthInfo.chargerWirelessOnline = false;
+        mUpdatesStopped = true;
+        Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+    }
+
+    private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
+        if (mUpdatesStopped) {
+            mUpdatesStopped = false;
+            copy(mHealthInfo, mLastHealthInfo);
+            Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+        }
+        if (mBatteryInputSuspended) {
+            PowerProperties.battery_input_suspended(false);
+            mBatteryInputSuspended = false;
+        }
+    }
+
+    private void suspendBatteryInput() {
+        if (!Build.IS_DEBUGGABLE) {
+            throw new SecurityException(
+                    "battery suspend_input is only supported on debuggable builds");
+        }
+        PowerProperties.battery_input_suspended(true);
+        mBatteryInputSuspended = true;
+    }
+
+    private void processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw) {
+        processValuesLocked(forceUpdate);
+        if (pw != null && forceUpdate) {
             pw.println(mSequence);
         }
     }
@@ -1363,6 +1384,41 @@
                 return mInvalidCharger;
             }
         }
+
+        @Override
+        public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.setChargerAcOnline(online, forceUpdate);
+        }
+
+        @Override
+        public void setBatteryLevel(int level, boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.setBatteryLevel(level, forceUpdate);
+        }
+
+        @Override
+        public void unplugBattery(boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.unplugBattery(forceUpdate, /* printWriter= */ null);
+        }
+
+        @Override
+        public void resetBattery(boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.resetBattery(forceUpdate, /* printWriter= */ null);
+        }
+
+        @Override
+        public void suspendBatteryInput() {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.suspendBatteryInput();
+        }
     }
 
     /**
@@ -1539,6 +1595,8 @@
                             if (Objects.equals(newService, oldService)) return;
 
                             Slog.i(TAG, "health: new instance registered " + mInstanceName);
+                            // #init() may be called with null callback. Skip null callbacks.
+                            if (mCallback == null) return;
                             mCallback.onRegistration(oldService, newService, mInstanceName);
                         } catch (NoSuchElementException | RemoteException ex) {
                             Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 709f5b2..688b33e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -85,6 +85,7 @@
 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.DataStallReportParcelable;
 import android.net.DnsResolverServiceManager;
 import android.net.ICaptivePortal;
@@ -175,6 +176,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.sysprop.NetworkProperties;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -186,10 +188,8 @@
 import android.util.SparseIntArray;
 
 import com.android.connectivity.aidl.INetworkAgent;
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
@@ -201,6 +201,7 @@
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.PermissionUtils;
 import com.android.server.connectivity.AutodestructReference;
+import com.android.server.connectivity.ConnectivityResources;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
 import com.android.server.connectivity.KeepaliveTracker;
@@ -316,6 +317,7 @@
     private boolean mRestrictBackground;
 
     private final Context mContext;
+    private final ConnectivityResources mResources;
     // The Context is created for UserHandle.ALL.
     private final Context mUserAllContext;
     private final Dependencies mDeps;
@@ -343,8 +345,7 @@
     private String mCurrentTcpBufferSizes;
 
     private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
-            new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
-                    NetworkAgentInfo.class });
+            new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
 
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -604,7 +605,6 @@
     private Intent mInitialBroadcast;
 
     private PowerManager.WakeLock mNetTransitionWakeLock;
-    private int mNetTransitionWakeLockTimeout;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
     // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
@@ -1012,6 +1012,13 @@
         }
 
         /**
+         * Get the {@link ConnectivityResources} to use in ConnectivityService.
+         */
+        public ConnectivityResources getResources(@NonNull Context ctx) {
+            return new ConnectivityResources(ctx);
+        }
+
+        /**
          * Create a HandlerThread to use in ConnectivityService.
          */
         public HandlerThread makeHandlerThread() {
@@ -1069,10 +1076,14 @@
          */
         public void reportNetworkInterfaceForTransports(Context context, String iface,
                 int[] transportTypes) {
-            final BatteryStatsManager  batteryStats =
+            final BatteryStatsManager batteryStats =
                     context.getSystemService(BatteryStatsManager.class);
             batteryStats.reportNetworkInterfaceForTransports(iface, transportTypes);
         }
+
+        public boolean getCellular464XlatEnabled() {
+            return NetworkProperties.isCellular464XlatEnabled().orElse(true);
+        }
     }
 
     public ConnectivityService(Context context) {
@@ -1089,13 +1100,15 @@
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
         mContext = Objects.requireNonNull(context, "missing Context");
+        mResources = deps.getResources(mContext);
         mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
 
         mMetricsLog = logger;
         mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultRequest();
         mDefaultRequest = new NetworkRequestInfo(
-                defaultInternetRequest, null, new Binder(),
+                defaultInternetRequest, null,
+                new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                 null /* attributionTags */);
         mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
         mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1149,8 +1162,6 @@
         final PowerManager powerManager = (PowerManager) context.getSystemService(
                 Context.POWER_SERVICE);
         mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_networkTransitionTimeout);
         mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -1217,10 +1228,6 @@
             }
         }
 
-        mWolSupportedInterfaces = new ArraySet(
-                mContext.getResources().getStringArray(
-                        com.android.internal.R.array.config_wakeonlan_supported_interfaces));
-
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1271,8 +1278,7 @@
                 new NetworkInfo(TYPE_NONE, 0, "", ""),
                 new LinkProperties(), new NetworkCapabilities(), 0, mContext,
                 null, new NetworkAgentConfig(), this, null,
-                null, 0, INVALID_UID,
-                mQosCallbackTracker);
+                null, 0, INVALID_UID, mQosCallbackTracker, mDeps);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1352,7 +1358,9 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    networkRequest, null, new Binder(),
+                    networkRequest, null,
+                    new Binder(),
+                    NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                     null /* attributionTags */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
@@ -1514,11 +1522,11 @@
         // 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
-                : filtered.getDetailedState();
-        filtered.setDetailedState(getLegacyLockdownState(state),
-                "" /* reason */, null /* extraInfo */);
+        if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
+            filtered.setDetailedState(DetailedState.BLOCKED, null /* reason */,
+                    null /* extraInfo */);
+        }
+        filterForLegacyLockdown(filtered);
         return filtered;
     }
 
@@ -1594,8 +1602,8 @@
         final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, false)
                 ? DetailedState.BLOCKED
                 : DetailedState.DISCONNECTED;
-        info.setDetailedState(getLegacyLockdownState(state),
-                "" /* reason */, null /* extraInfo */);
+        info.setDetailedState(state, null /* reason */, null /* extraInfo */);
+        filterForLegacyLockdown(info);
         return info;
     }
 
@@ -1713,8 +1721,8 @@
                 result.put(
                         nai.network,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, mDeps.getCallingUid(), callingPackageName,
-                                callingAttributionTag));
+                                nc, false /* includeLocationSensitiveInfo */,
+                                mDeps.getCallingUid(), callingPackageName, callingAttributionTag));
             }
         }
 
@@ -1727,7 +1735,9 @@
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    nc,
+                                    false /* includeLocationSensitiveInfo */,
+                                    mDeps.getCallingUid(), callingPackageName,
                                     callingAttributionTag));
                 }
             }
@@ -1809,6 +1819,7 @@
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
+                false /* includeLocationSensitiveInfo */,
                 mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
@@ -1842,8 +1853,8 @@
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
-            @Nullable String callingAttributionTag) {
+            @Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo,
+            int callerUid, @NonNull String callerPkgName, @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
@@ -1851,7 +1862,9 @@
         final NetworkCapabilities newNc;
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
-        if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
+        if (includeLocationSensitiveInfo
+                && nc.getTransportInfo() != null
+                && nc.getTransportInfo().hasLocationSensitiveFields()) {
             hasLocationPermission =
                     hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
             newNc = new NetworkCapabilities(nc, hasLocationPermission);
@@ -1868,6 +1881,16 @@
             // Owner UIDs already checked above. No need to re-check.
             return newNc;
         }
+        // If the caller does not want location sensitive data & target SDK >= S, then mask info.
+        // Else include the owner UID iff the caller has location permission to provide backwards
+        // compatibility for older apps.
+        if (!includeLocationSensitiveInfo
+                && isTargetSdkAtleast(
+                        Build.VERSION_CODES.S, callerUid, callerPkgName)) {
+            newNc.setOwnerUid(INVALID_UID);
+            return newNc;
+        }
+
         if (hasLocationPermission == null) {
             // Location permission not checked yet, check now for masking owner UID.
             hasLocationPermission =
@@ -2389,9 +2412,7 @@
         mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
     }
 
-    // Public because it's used by mLockdownTracker.
-    public void sendConnectedBroadcast(NetworkInfo info) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
+    private void sendConnectedBroadcast(NetworkInfo info) {
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
@@ -2888,22 +2909,6 @@
             super(looper);
         }
 
-        private boolean maybeHandleAsyncChannelMessage(Message msg) {
-            switch (msg.what) {
-                default:
-                    return false;
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
-                    handleAsyncChannelHalfConnect(msg);
-                    break;
-                }
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                    handleAsyncChannelDisconnected(msg);
-                    break;
-                }
-            }
-            return true;
-        }
-
         private void maybeHandleNetworkAgentMessage(Message msg) {
             final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
             final NetworkAgentInfo nai = arg.first;
@@ -3195,8 +3200,7 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (!maybeHandleAsyncChannelMessage(msg)
-                    && !maybeHandleNetworkMonitorMessage(msg)
+            if (!maybeHandleNetworkMonitorMessage(msg)
                     && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
@@ -3460,21 +3464,6 @@
         return false;
     }
 
-    private void handleAsyncChannelHalfConnect(Message msg) {
-        ensureRunningOnConnectivityServiceThread();
-        if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
-            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                if (VDBG) log("NetworkFactory connected");
-                // Finish setting up the full connection
-                NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                sendAllRequestsToProvider(npi);
-            } else {
-                loge("Error connecting NetworkFactory");
-                mNetworkProviderInfos.remove(msg.obj);
-            }
-        }
-    }
-
     private void handleNetworkAgentRegistered(Message msg) {
         final NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
         if (!mNetworkAgentInfos.contains(nai)) {
@@ -3505,14 +3494,6 @@
         }
     }
 
-    // This is a no-op if it's called with a message designating a provider that has
-    // already been destroyed, because its reference will not be found in the relevant
-    // maps.
-    private void handleAsyncChannelDisconnected(Message msg) {
-        NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
-        if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
-    }
-
     // Destroys a network, remove references to it from the internal state managed by
     // ConnectivityService, free its interfaces and clean up.
     // Must be called on the Handler thread.
@@ -4444,7 +4425,8 @@
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
-                    handleApplyDefaultProxy((ProxyInfo)msg.obj);
+                    final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
+                    handleApplyDefaultProxy(arg.second);
                     break;
                 }
                 case EVENT_REGISTER_NETWORK_PROVIDER: {
@@ -4621,7 +4603,9 @@
         }
         mWakelockLogs.log("ACQUIRE for " + forWhom);
         Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
-        mHandler.sendMessageDelayed(msg, mNetTransitionWakeLockTimeout);
+        final int lockTimeout = mResources.get().getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        mHandler.sendMessageDelayed(msg, lockTimeout);
     }
 
     // Called when we gain a new default network to release the network transition wakelock in a
@@ -5036,8 +5020,8 @@
         // The legacy lockdown VPN always uses the default network.
         // If the VPN's underlying network is no longer the current default network, it means that
         // the default network has just switched, and the VPN is about to disconnect.
-        // Report that the VPN is not connected, so when the state of NetworkInfo objects
-        // overwritten by getLegacyLockdownState will be set to CONNECTING and not CONNECTED.
+        // Report that the VPN is not connected, so the state of NetworkInfo objects overwritten
+        // by filterForLegacyLockdown will be set to CONNECTING and not CONNECTED.
         final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
         if (defaultNetwork == null || !defaultNetwork.network.equals(underlying[0])) {
             return null;
@@ -5046,6 +5030,9 @@
         return nai;
     };
 
+    // TODO: move all callers to filterForLegacyLockdown and delete this method.
+    // This likely requires making sendLegacyNetworkBroadcast take a NetworkInfo object instead of
+    // just a DetailedState object.
     private DetailedState getLegacyLockdownState(DetailedState origState) {
         if (origState != DetailedState.CONNECTED) {
             return origState;
@@ -5055,6 +5042,23 @@
                 : DetailedState.CONNECTED;
     }
 
+    private void filterForLegacyLockdown(NetworkInfo ni) {
+        if (!mLockdownEnabled || !ni.isConnected()) return;
+        // The legacy lockdown VPN replaces the state of every network in CONNECTED state with the
+        // state of its VPN. This is to ensure that when an underlying network connects, apps will
+        // not see a CONNECTIVITY_ACTION broadcast for a network in state CONNECTED until the VPN
+        // comes up, at which point there is a new CONNECTIVITY_ACTION broadcast for the underlying
+        // network, this time with a state of CONNECTED.
+        //
+        // Now that the legacy lockdown code lives in ConnectivityService, and no longer has access
+        // to the internal state of the Vpn object, always replace the state with CONNECTING. This
+        // is not too far off the truth, since an always-on VPN, when not connected, is always
+        // trying to reconnect.
+        if (getLegacyLockdownNai() == null) {
+            ni.setDetailedState(DetailedState.CONNECTING, "", null);
+        }
+    }
+
     @Override
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
@@ -5133,8 +5137,8 @@
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
-        NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
+        NetworkProviderInfo(String name, Messenger messenger, int providerId,
+                @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
@@ -5228,6 +5232,7 @@
         private final IBinder mBinder;
         final int mPid;
         final int mUid;
+        final @NetworkCallback.Flag int mCallbackFlags;
         @Nullable
         final String mCallingAttributionTag;
         // In order to preserve the mapping of NetworkRequest-to-callback when apps register
@@ -5275,17 +5280,26 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            /**
+             * Location sensitive data not included in pending intent. Only included in
+             * {@link NetworkCallback}.
+             */
+            mCallbackFlags = NetworkCallback.FLAG_NONE;
             mCallingAttributionTag = callingAttributionTag;
         }
 
         NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
-            this(Collections.singletonList(r), r, m, binder, callingAttributionTag);
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
+            this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag);
         }
 
         NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
                 @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
             super();
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
@@ -5296,6 +5310,7 @@
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = callbackFlags;
             mCallingAttributionTag = callingAttributionTag;
 
             try {
@@ -5337,6 +5352,7 @@
             mUid = nri.mUid;
             mPendingIntent = nri.mPendingIntent;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = nri.mCallbackFlags;
             mCallingAttributionTag = nri.mCallingAttributionTag;
         }
 
@@ -5386,7 +5402,8 @@
                     + " callback request Id: "
                     + mNetworkRequestForCallback.requestId
                     + " " + mRequests
-                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
+                    + "callback flags: " + mCallbackFlags;
         }
     }
 
@@ -5470,13 +5487,13 @@
         }
     }
 
-    private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
-        final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
+    private boolean isTargetSdkAtleast(int version, int callingUid,
+            @NonNull String callingPackageName) {
+        final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
         final PackageManager pm =
                 mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
         try {
-            final int callingVersion = pm.getApplicationInfo(
-                    callingPackageName, 0 /* flags */).targetSdkVersion;
+            final int callingVersion = pm.getTargetSdkVersion(callingPackageName);
             if (callingVersion < version) return false;
         } catch (PackageManager.NameNotFoundException e) { }
         return true;
@@ -5485,10 +5502,11 @@
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
-            int legacyType, @NonNull String callingPackageName,
+            int legacyType, int callbackFlags, @NonNull String callingPackageName,
             @Nullable String callingAttributionTag) {
         if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
-            if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+            if (isTargetSdkAtleast(Build.VERSION_CODES.M, mDeps.getCallingUid(),
+                    callingPackageName)) {
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
@@ -5525,6 +5543,8 @@
                 //  request if the app changes network state. http://b/29964605
                 enforceMeteredApnPolicy(networkCapabilities);
                 break;
+            case TRACK_BEST:
+                throw new UnsupportedOperationException("Not implemented yet");
             default:
                 throw new IllegalArgumentException("Unsupported request type " + reqType);
         }
@@ -5548,7 +5568,7 @@
         final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
         final NetworkRequestInfo nri = getNriToRegister(
-                networkRequest, messenger, binder, callingAttributionTag);
+                networkRequest, messenger, binder, callbackFlags, callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
         // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
@@ -5583,6 +5603,7 @@
      */
     private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr,
             @Nullable final Messenger msgr, @Nullable final IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
             @Nullable String callingAttributionTag) {
         final List<NetworkRequest> requests;
         if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) {
@@ -5591,7 +5612,8 @@
         } else {
             requests = Collections.singletonList(nr);
         }
-        return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag);
+        return new NetworkRequestInfo(
+                requests, nr, msgr, binder, callbackFlags, callingAttributionTag);
     }
 
     private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
@@ -5717,8 +5739,9 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
-            @Nullable String callingAttributionTag) {
+            Messenger messenger, IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
+            @NonNull String callingPackageName, @NonNull String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5739,7 +5762,8 @@
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
         NetworkRequestInfo nri =
-                new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag);
+                new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags,
+                        callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5808,8 +5832,7 @@
     public int registerNetworkProvider(Messenger messenger, String name) {
         enforceNetworkFactoryOrSettingsPermission();
         NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
-                null /* asyncChannel */, nextNetworkProviderId(),
-                () -> unregisterNetworkProvider(messenger));
+                nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
         return npi.providerId;
     }
@@ -6130,7 +6153,7 @@
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
                 currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
-                this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker);
+                this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps);
 
         // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
         processCapabilitiesFromAgent(nai, nc);
@@ -6496,6 +6519,11 @@
     }
 
     private void updateWakeOnLan(@NonNull LinkProperties lp) {
+        if (mWolSupportedInterfaces == null) {
+            mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
+                    com.android.connectivity.resources.R.array
+                            .config_wakeonlan_supported_interfaces));
+        }
         lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
     }
 
@@ -7068,6 +7096,8 @@
         if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
             putParcelable(bundle, networkAgent.network);
         }
+        final boolean includeLocationSensitiveInfo =
+                (nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 final NetworkCapabilities nc =
@@ -7076,7 +7106,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nc, includeLocationSensitiveInfo, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -7096,7 +7127,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                netCap, includeLocationSensitiveInfo, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 break;
             }
@@ -7938,6 +7970,7 @@
         // and is still connected.
         NetworkInfo info = new NetworkInfo(nai.networkInfo);
         info.setType(type);
+        filterForLegacyLockdown(info);
         if (state != DetailedState.DISCONNECTED) {
             info.setDetailedState(state, null, info.getExtraInfo());
             sendConnectedBroadcast(info);
@@ -8052,8 +8085,8 @@
     @Override
     public String getCaptivePortalServerUrl() {
         enforceNetworkStackOrSettingsPermission();
-        String settingUrl = mContext.getResources().getString(
-                R.string.config_networkCaptivePortalServerUrl);
+        String settingUrl = mResources.get().getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
 
         if (!TextUtils.isEmpty(settingUrl)) {
             return settingUrl;
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index a353519..96ff900 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.util.Log;
 
@@ -51,4 +53,9 @@
             publishBinderService(Context.CONTEXTHUB_SERVICE, mContextHubService);
         }
     }
+
+    @Override
+    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
+        mContextHubService.onUserChanged();
+    }
 }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 81d4b9d..4c3c6ef 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -56,6 +56,7 @@
 import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Range;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -756,13 +757,9 @@
         }
     }
 
-    // These values have been reserved in NetIdManager
-    @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
-
-    public static final int TUN_INTF_NETID_RANGE = 0x0400;
-
     private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
-    private int mNextTunnelNetIdIndex = 0;
+    final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
+    private int mNextTunnelNetId = mNetIdRange.getLower();
 
     /**
      * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
@@ -775,11 +772,13 @@
      */
     @VisibleForTesting
     int reserveNetId() {
+        final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
         synchronized (mTunnelNetIds) {
-            for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
-                int index = mNextTunnelNetIdIndex;
-                int netId = index + TUN_INTF_NETID_START;
-                if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+            for (int i = 0; i < range; i++) {
+                final int netId = mNextTunnelNetId;
+                if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
+                    mNextTunnelNetId = mNetIdRange.getLower();
+                }
                 if (!mTunnelNetIds.get(netId)) {
                     mTunnelNetIds.put(netId, true);
                     return netId;
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
index 097fb3a..61925c8 100644
--- a/services/core/java/com/android/server/NetIdManager.java
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.net.ConnectivityManager;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -31,7 +32,7 @@
     // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
     public static final int MIN_NET_ID = 100; // some reserved marks
     // Top IDs reserved by IpSecService
-    public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+    public static final int MAX_NET_ID = ConnectivityManager.getIpSecNetIdRange().getLower() - 1;
 
     @GuardedBy("mNetIdInUse")
     private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index e12586b..c983600 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -18,6 +18,7 @@
 
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *Battery* = file:/BATTERY_STATS_OWNERS
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index f2782f6..18d47c6 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -51,6 +51,7 @@
 import android.hardware.ISensorPrivacyListener;
 import android.hardware.ISensorPrivacyManager;
 import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -67,6 +68,7 @@
 import android.service.SensorPrivacyUserProto;
 import android.text.Html;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
@@ -80,6 +82,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FunctionalUtils;
 import com.android.internal.util.XmlUtils;
@@ -137,6 +140,8 @@
     private final ActivityManager mActivityManager;
     private final ActivityTaskManager mActivityTaskManager;
 
+    private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
+
     public SensorPrivacyService(Context context) {
         super(context);
         mUserManagerInternal = getLocalService(UserManagerInternal.class);
@@ -148,6 +153,9 @@
     @Override
     public void onStart() {
         publishBinderService(Context.SENSOR_PRIVACY_SERVICE, mSensorPrivacyServiceImpl);
+        mSensorPrivacyManagerInternal = new SensorPrivacyManagerInternalImpl();
+        publishLocalService(SensorPrivacyManagerInternal.class,
+                mSensorPrivacyManagerInternal);
     }
 
     class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements
@@ -1108,6 +1116,7 @@
         }
 
         public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+            mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
             SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
                     mIndividualSensorListeners.get(userId);
             if (listenersForUser == null) {
@@ -1168,4 +1177,87 @@
             c.accept(userIds[i]);
         }
     }
+
+    private class SensorPrivacyManagerInternalImpl extends SensorPrivacyManagerInternal {
+
+        private ArrayMap<Integer, ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>>>
+                mListeners = new ArrayMap<>();
+        private ArrayMap<Integer, ArraySet<OnUserSensorPrivacyChangedListener>> mAllUserListeners =
+                new ArrayMap<>();
+
+        private final Object mLock = new Object();
+
+        private void dispatch(int userId, int sensor, boolean enabled) {
+            synchronized (mLock) {
+                ArraySet<OnUserSensorPrivacyChangedListener> allUserSensorListeners =
+                        mAllUserListeners.get(sensor);
+                if (allUserSensorListeners != null) {
+                    for (int i = 0; i < allUserSensorListeners.size(); i++) {
+                        OnUserSensorPrivacyChangedListener listener =
+                                allUserSensorListeners.valueAt(i);
+                        BackgroundThread.getHandler().post(() ->
+                                listener.onSensorPrivacyChanged(userId, enabled));
+                    }
+                }
+
+                ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners =
+                        mListeners.get(userId);
+                if (userSensorListeners != null) {
+                    ArraySet<OnSensorPrivacyChangedListener> sensorListeners =
+                            userSensorListeners.get(sensor);
+                    if (sensorListeners != null) {
+                        for (int i = 0; i < sensorListeners.size(); i++) {
+                            OnSensorPrivacyChangedListener listener = sensorListeners.valueAt(i);
+                            BackgroundThread.getHandler().post(() ->
+                                    listener.onSensorPrivacyChanged(enabled));
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean isSensorPrivacyEnabled(int userId, int sensor) {
+            return SensorPrivacyService.this
+                    .mSensorPrivacyServiceImpl.isIndividualSensorPrivacyEnabled(userId, sensor);
+        }
+
+        @Override
+        public void addSensorPrivacyListener(int userId, int sensor,
+                OnSensorPrivacyChangedListener listener) {
+            synchronized (mLock) {
+                ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners =
+                        mListeners.get(userId);
+                if (userSensorListeners == null) {
+                    userSensorListeners = new ArrayMap<>();
+                    mListeners.put(userId, userSensorListeners);
+                }
+
+                ArraySet<OnSensorPrivacyChangedListener> sensorListeners =
+                        userSensorListeners.get(sensor);
+                if (sensorListeners == null) {
+                    sensorListeners = new ArraySet<>();
+                    userSensorListeners.put(sensor, sensorListeners);
+                }
+
+                sensorListeners.add(listener);
+            }
+        }
+
+        @Override
+        public void addSensorPrivacyListenerForAllUsers(int sensor,
+                OnUserSensorPrivacyChangedListener listener) {
+            synchronized (mLock) {
+                ArraySet<OnUserSensorPrivacyChangedListener> sensorListeners =
+                        mAllUserListeners.get(sensor);
+                if (sensorListeners == null) {
+                    sensorListeners = new ArraySet<>();
+                    mAllUserListeners.put(sensor, sensorListeners);
+                }
+
+                sensorListeners.add(listener);
+            }
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 27b648e..8b5bb1d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -962,7 +962,7 @@
             }
 
             int delay = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
-                    ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 0);
+                    ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 5000);
             Slog.v(TAG, "getAnrDelayMillis for " + packageName + ". " + delay + "ms");
             return delay;
         }
@@ -1791,7 +1791,7 @@
     public StorageManagerService(Context context) {
         sSelf = this;
         mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
-                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
         mContext = context;
         mResolver = mContext.getContentResolver();
         mCallbacks = new Callbacks(FgThread.get().getLooper());
@@ -4553,6 +4553,13 @@
         private final List<StorageManagerInternal.ResetListener> mResetListeners =
                 new ArrayList<>();
 
+        @Override
+        public boolean isFuseMounted(int userId) {
+            synchronized (mLock) {
+                return mFuseMountedUser.contains(userId);
+            }
+        }
+
         /**
          * Check if fuse is running in target user, if it's running then setup its storage dirs.
          * Return true if storage dirs are mounted.
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 95af842..a09dbc7 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -21,7 +21,9 @@
                 }
             ],
             "file_patterns": ["NotificationManagerService\\.java"]
-        },
+        }
+    ],
+    "presubmit-large": [
         {
             "name": "CtsScopedStorageCoreHostTest",
             "file_patterns": ["StorageManagerService\\.java"]
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a9904ba..5adbdff 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -318,7 +318,8 @@
 
     private int[] mDataEnabledReason;
 
-    private Map<Integer, Long> mAllowedNetworkTypesList;
+    private int[] mAllowedNetworkTypeReason;
+    private long[] mAllowedNetworkTypeValue;
 
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -383,7 +384,8 @@
     private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
         return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
     }
 
     private static final int MSG_USER_SWITCHED = 1;
@@ -527,6 +529,8 @@
         mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
         mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
         mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
+        mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
+        mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
 
         // ds -> ss switch.
         if (mNumPhones < oldNumPhones) {
@@ -571,6 +575,8 @@
             mIsDataEnabled[i] = false;
             mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+            mAllowedNetworkTypeReason[i] = -1;
+            mAllowedNetworkTypeValue[i] = -1;
         }
     }
 
@@ -630,9 +636,11 @@
         mBarringInfo = new ArrayList<>();
         mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
         mPhysicalChannelConfigs = new ArrayList<>();
-        mAllowedNetworkTypesList = new HashMap<>();
+        mAllowedNetworkTypeReason = new int[numPhones];
+        mAllowedNetworkTypeValue = new long[numPhones];
         mIsDataEnabled = new boolean[numPhones];
         mDataEnabledReason = new int[numPhones];
+
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -665,6 +673,8 @@
             mIsDataEnabled[i] = false;
             mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+            mAllowedNetworkTypeReason[i] = -1;
+            mAllowedNetworkTypeValue[i] = -1;
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1172,14 +1182,6 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(
-                        TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
-                    try {
-                        r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
             }
         }
     }
@@ -2454,18 +2456,19 @@
      *
      * @param phoneId the phone id.
      * @param subId the subId.
-     * @param allowedNetworkTypesList Map associating all allowed network type reasons with reason's
-     *                                allowed network type values.
+     * @param reason the allowed network type reason.
+     * @param allowedNetworkType the allowed network type value.
      */
-    public void notifyAllowedNetworkTypesChanged(int phoneId, int subId,
-            Map allowedNetworkTypesList) {
+    public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, int reason,
+            long allowedNetworkType) {
         if (!checkNotifyPermission("notifyAllowedNetworkTypesChanged()")) {
             return;
         }
 
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                mAllowedNetworkTypesList = allowedNetworkTypesList;
+                mAllowedNetworkTypeReason[phoneId] = reason;
+                mAllowedNetworkTypeValue[phoneId] = allowedNetworkType;
 
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
@@ -2473,10 +2476,12 @@
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (VDBG) {
-                                log("notifyAllowedNetworkTypesChanged: AllowedNetworkTypesList= "
-                                        + mAllowedNetworkTypesList.toString());
+                                log("notifyAllowedNetworkTypesChanged: reason= " + reason
+                                        + ", allowed network type:"
+                                        + TelephonyManager.convertNetworkTypeBitmaskToString(
+                                        allowedNetworkType));
                             }
-                            r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+                            r.callback.onAllowedNetworkTypesChanged(reason, allowedNetworkType);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -2531,6 +2536,8 @@
                 pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
                 pw.println("mIsDataEnabled=" + mIsDataEnabled);
                 pw.println("mDataEnabledReason=" + mDataEnabledReason);
+                pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
+                pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index ee61067..f566277 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -90,7 +90,12 @@
         mCm = mContext.getSystemService(ConnectivityManager.class);
         mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
                 TEST_NETWORK_PROVIDER_NAME);
-        mCm.registerNetworkProvider(mNetworkProvider);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mCm.registerNetworkProvider(mNetworkProvider);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index cd3892d..140f24f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -666,6 +666,10 @@
             @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
         requireNonNull(listener, "listener was null");
 
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_FACTORY,
+                "Must have permission NETWORK_FACTORY to unregister a policy listener");
+
         Binder.withCleanCallingIdentity(() -> {
             synchronized (mLock) {
                 PolicyListenerBinderDeath listenerBinderDeath =
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 68c4a73..673749c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1841,7 +1841,7 @@
                     ServiceState stracker = r.getTracker();
                     if (stracker != null) {
                         stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
-                                r.lastActivity);
+                                SystemClock.uptimeMillis());
                     }
                 }
                 if (alreadyStartedOp) {
@@ -1863,7 +1863,7 @@
                 ServiceState stracker = r.getTracker();
                 if (stracker != null) {
                     stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
-                            r.lastActivity);
+                            SystemClock.uptimeMillis());
                 }
                 mAm.mAppOpsService.finishOperation(
                         AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3765,6 +3765,7 @@
             }
         }
 
+        final long now = SystemClock.uptimeMillis();
         // Check to see if the service had been started as foreground, but being
         // brought down before actually showing a notification.  That is not allowed.
         if (r.fgRequired) {
@@ -3774,8 +3775,7 @@
             r.fgWaiting = false;
             ServiceState stracker = r.getTracker();
             if (stracker != null) {
-                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
-                        r.lastActivity);
+                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
             }
             mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
@@ -3834,8 +3834,7 @@
             decActiveForegroundAppLocked(smap, r);
             ServiceState stracker = r.getTracker();
             if (stracker != null) {
-                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
-                        r.lastActivity);
+                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
             }
             mAm.mAppOpsService.finishOperation(
                     AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3902,7 +3901,6 @@
         }
 
         int memFactor = mAm.mProcessStats.getMemFactorLocked();
-        long now = SystemClock.uptimeMillis();
         if (r.tracker != null) {
             r.tracker.setStarted(false, memFactor, now);
             r.tracker.setBound(false, memFactor, now);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8ea6194..06a1abb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -44,11 +44,11 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
-import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Process.BLUETOOTH_UID;
@@ -273,6 +273,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalMetrics;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
@@ -7697,18 +7700,32 @@
      */
     void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
             ApplicationErrorReport.CrashInfo crashInfo) {
-        boolean isPackageLoading = false;
+        boolean isIncremental = false;
+        float loadingProgress = 1;
+        long millisSinceOldestPendingRead = 0;
         // Notify package manager service to possibly update package state
         if (r != null && r.info != null && r.info.packageName != null) {
+            final String codePath = r.info.getCodePath();
             mPackageManagerInt.notifyPackageCrashOrAnr(r.info.packageName);
             IncrementalStatesInfo incrementalStatesInfo =
                     mPackageManagerInt.getIncrementalStatesInfo(r.info.packageName, r.uid,
                             r.userId);
-            isPackageLoading = incrementalStatesInfo.isLoading();
-            if (isPackageLoading) {
-                // Report in the main log that the package is still loading
-                Slog.e(TAG, "App crashed when package " + r.info.packageName + " is "
-                        + ((int) (incrementalStatesInfo.getProgress() * 100)) + "% loaded.");
+            if (incrementalStatesInfo != null) {
+                loadingProgress = incrementalStatesInfo.getProgress();
+            }
+            isIncremental = IncrementalManager.isIncrementalPath(codePath);
+            if (isIncremental) {
+                // Report in the main log about the incremental package
+                Slog.e(TAG, "App crashed on incremental package " + r.info.packageName
+                        + " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
+                final IBinder incrementalService = ServiceManager.getService(
+                        Context.INCREMENTAL_SERVICE);
+                if (incrementalService != null) {
+                    final IncrementalManager incrementalManager = new IncrementalManager(
+                            IIncrementalService.Stub.asInterface(incrementalService));
+                    IncrementalMetrics metrics = incrementalManager.getMetrics(codePath);
+                    millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead();
+                }
             }
         }
 
@@ -7737,7 +7754,7 @@
                 processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
                         : (r != null) ? r.getProcessClassEnum()
                                       : ServerProtoEnums.ERROR_SOURCE_UNKNOWN,
-                isPackageLoading
+                isIncremental, loadingProgress, millisSinceOldestPendingRead
         );
 
         final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dbfa7f3..6b9fc07 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -32,6 +32,7 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.os.BatteryManagerInternal;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.BatteryUsageStats;
@@ -184,6 +185,8 @@
                 }
             };
 
+    private BatteryManagerInternal mBatteryManagerInternal;
+
     private void populatePowerEntityMaps() {
         PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
         if (entities == null) {
@@ -370,6 +373,7 @@
                 Slog.e(TAG, "Could not register PowerStatsInternal");
             }
         }
+        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
 
         Watchdog.getInstance().addMonitor(this);
 
@@ -2715,4 +2719,44 @@
             });
         }
     }
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     */
+    @Override
+    public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        mBatteryManagerInternal.setChargerAcOnline(online, forceUpdate);
+    }
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     */
+    @Override
+    public void setBatteryLevel(int level, boolean forceUpdate) {
+        mBatteryManagerInternal.setBatteryLevel(level, forceUpdate);
+    }
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     */
+    @Override
+    public void unplugBattery(boolean forceUpdate) {
+        mBatteryManagerInternal.unplugBattery(forceUpdate);
+    }
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     */
+    @Override
+    public void resetBattery(boolean forceUpdate) {
+        mBatteryManagerInternal.resetBattery(forceUpdate);
+    }
+
+    /**
+     * Suspend charging even if plugged in.
+     */
+    @Override
+    public void suspendBatteryInput() {
+        mBatteryManagerInternal.suspendBatteryInput();
+    }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 81c4c86..e79f096 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1416,7 +1416,7 @@
             }
         }
         if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
-            r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+                r.requiredPermissions != null && r.requiredPermissions.length > 0) {
             for (int i = 0; i < r.requiredPermissions.length; i++) {
                 String requiredPermission = r.requiredPermissions[i];
                 try {
@@ -1424,7 +1424,7 @@
                             checkPermission(requiredPermission,
                                     info.activityInfo.applicationInfo.packageName,
                                     UserHandle
-                                            .getUserId(info.activityInfo.applicationInfo.uid));
+                                    .getUserId(info.activityInfo.applicationInfo.uid));
                 } catch (RemoteException e) {
                     perm = PackageManager.PERMISSION_DENIED;
                 }
@@ -1439,36 +1439,18 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                        null /* default featureId */,
-                        "Broadcast delivered to " + info.activityInfo.name)
-                        != AppOpsManager.MODE_ALLOWED) {
-                    Slog.w(TAG, "Appop Denial: receiving "
-                            + r.intent + " to "
-                            + component.flattenToShortString()
-                            + " requires appop " + AppOpsManager.permissionToOp(
-                            requiredPermission)
-                            + " due to sender " + r.callerPackage
-                            + " (uid " + r.callingUid + ")");
-                    skip = true;
-                    break;
+                if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
+                    if (!noteOpForManifestReceiver(appOp, r, info, component)) {
+                        skip = true;
+                        break;
+                    }
                 }
             }
         }
-        if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
-                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                null  /* default featureId */, "Broadcast delivered to " + info.activityInfo.name)
-                != AppOpsManager.MODE_ALLOWED) {
-            Slog.w(TAG, "Appop Denial: receiving "
-                    + r.intent + " to "
-                    + component.flattenToShortString()
-                    + " requires appop " + AppOpsManager.opToName(r.appOp)
-                    + " due to sender " + r.callerPackage
-                    + " (uid " + r.callingUid + ")");
-            skip = true;
+        if (!skip && r.appOp != AppOpsManager.OP_NONE) {
+            if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
+                skip = true;
+            }
         }
         boolean isSingleton = false;
         try {
@@ -1717,6 +1699,40 @@
         mPendingBroadcastRecvIndex = recIdx;
     }
 
+    private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
+            ComponentName component) {
+        if (info.activityInfo.attributionTags == null) {
+            return noteOpForManifestReceiverInner(appOp, r, info, component, null);
+        } else {
+            // Attribution tags provided, noteOp each tag
+            for (String tag : info.activityInfo.attributionTags) {
+                if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
+            ComponentName component, String tag) {
+        if (mService.getAppOpsManager().noteOpNoThrow(appOp,
+                    info.activityInfo.applicationInfo.uid,
+                    info.activityInfo.packageName,
+                    tag,
+                    "Broadcast delivered to " + info.activityInfo.name)
+                != AppOpsManager.MODE_ALLOWED) {
+            Slog.w(TAG, "Appop Denial: receiving "
+                    + r.intent + " to "
+                    + component.flattenToShortString()
+                    + " requires appop " + AppOpsManager.opToName(appOp)
+                    + " due to sender " + r.callerPackage
+                    + " (uid " + r.callingUid + ")");
+            return false;
+        }
+        return true;
+    }
+
     private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
         if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
             return;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index d03a47a..93f30cc 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -26,12 +26,18 @@
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.PackageManagerInternal;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
+import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalMetrics;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Slog;
@@ -294,14 +300,31 @@
         }
 
         // Check if package is still being loaded
-        boolean isPackageLoading = false;
+        boolean isIncremental = false;
+        float loadingProgress = 1;
+        long millisSinceOldestPendingRead = 0;
         final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
         if (aInfo != null && aInfo.packageName != null) {
             IncrementalStatesInfo incrementalStatesInfo =
                     packageManagerInternal.getIncrementalStatesInfo(
                             aInfo.packageName, mApp.uid, mApp.userId);
             if (incrementalStatesInfo != null) {
-                isPackageLoading = incrementalStatesInfo.isLoading();
+                loadingProgress = incrementalStatesInfo.getProgress();
+            }
+            final String codePath = aInfo.getCodePath();
+            isIncremental = IncrementalManager.isIncrementalPath(codePath);
+            if (isIncremental) {
+                // Report in the main log that the incremental package is still loading
+                Slog.e(TAG, "App crashed on incremental package " + aInfo.packageName
+                        + " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
+                final IBinder incrementalService = ServiceManager.getService(
+                        Context.INCREMENTAL_SERVICE);
+                if (incrementalService != null) {
+                    final IncrementalManager incrementalManager = new IncrementalManager(
+                            IIncrementalService.Stub.asInterface(incrementalService));
+                    IncrementalMetrics metrics = incrementalManager.getMetrics(codePath);
+                    millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead();
+                }
             }
         }
 
@@ -322,10 +345,8 @@
             info.append("Parent: ").append(parentShortComponentName).append("\n");
         }
 
-        if (isPackageLoading) {
-            // Report in the main log that the package is still loading
-            final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
-                    aInfo.packageName, mApp.uid, mApp.userId).getProgress();
+        if (isIncremental) {
+            // Report in the main log about the incremental package
             info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
         }
 
@@ -412,7 +433,8 @@
                         ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
                         : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
                 mApp.getProcessClassEnum(),
-                (mApp.info != null) ? mApp.info.packageName : "", isPackageLoading);
+                (mApp.info != null) ? mApp.info.packageName : "",
+                isIncremental, loadingProgress, millisSinceOldestPendingRead);
         final ProcessRecord parentPr = parentProcess != null
                 ? (ProcessRecord) parentProcess.mOwner : null;
         mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 38330fe..4972aa7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -786,7 +786,7 @@
         mAppDataIsolationEnabled =
                 SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
         mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
-                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
+                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
         mAppDataIsolationAllowlistedApps = new ArrayList<>(
                 SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
 
@@ -2315,11 +2315,12 @@
             StorageManagerInternal storageManagerInternal = LocalServices.getService(
                     StorageManagerInternal.class);
             if (needsStorageDataIsolation(storageManagerInternal, app)) {
-                bindMountAppStorageDirs = true;
-                if (pkgDataInfoMap == null ||
-                        !storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
-                        app.processName)) {
-                    // Cannot prepare Android/app and Android/obb directory or inode == 0,
+                // We will run prepareStorageDirs() after we trigger zygote fork, so it won't
+                // slow down app starting speed as those dirs might not be cached.
+                if (pkgDataInfoMap != null && storageManagerInternal.isFuseMounted(userId)) {
+                    bindMountAppStorageDirs = true;
+                } else {
+                    // Fuse is not mounted or inode == 0,
                     // so we won't mount it in zygote, but resume the mount after unlocking device.
                     app.setBindMountPending(true);
                     bindMountAppStorageDirs = false;
@@ -2367,6 +2368,13 @@
                         allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
             }
+            // This runs after Process.start() as this method may block app process starting time
+            // if dir is not cached. Running this method after Process.start() can make it
+            // cache the dir asynchronously, so zygote can use it without waiting for it.
+            if (bindMountAppStorageDirs) {
+                storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+                        app.processName);
+            }
             checkSlow(startTime, "startProcess: returned from zygote!");
             return startResult;
         } finally {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index caf2510..ec2020f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -122,7 +122,6 @@
 import java.util.Objects;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
 
 /**
  * Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
@@ -518,18 +517,12 @@
             if (!mInjector.getUserManager().isPreCreated(userId)) {
                 mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
                         userId, 0));
-                Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
-                        | Intent.FLAG_RECEIVER_OFFLOAD
-                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-                mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
-                        new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                        AppOpsManager.OP_NONE,
-                        getTemporaryAppAllowlistBroadcastOptions(REASON_LOCKED_BOOT_COMPLETED)
-                                .toBundle(), true,
-                        false, MY_PID, SYSTEM_UID,
-                        Binder.getCallingUid(), Binder.getCallingPid(), userId);
+                // In case of headless system user mode, do not send boot complete broadcast for
+                // system user as it is sent by sendBootCompleted call.
+                if (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
+                    // ACTION_LOCKED_BOOT_COMPLETED
+                    sendLockedBootCompletedBroadcast(resultTo, userId);
+                }
             }
         }
 
@@ -552,6 +545,21 @@
         }
     }
 
+    private void sendLockedBootCompletedBroadcast(IIntentReceiver receiver, @UserIdInt int userId) {
+        final Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                | Intent.FLAG_RECEIVER_OFFLOAD
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mInjector.broadcastIntent(intent, null, receiver, 0, null, null,
+                new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+                AppOpsManager.OP_NONE,
+                getTemporaryAppAllowlistBroadcastOptions(REASON_LOCKED_BOOT_COMPLETED)
+                        .toBundle(), true,
+                false, MY_PID, SYSTEM_UID,
+                Binder.getCallingUid(), Binder.getCallingPid(), userId);
+    }
+
     /**
      * Step from {@link UserState#STATE_RUNNING_LOCKED} to
      * {@link UserState#STATE_RUNNING_UNLOCKING}.
@@ -2167,26 +2175,22 @@
     }
 
     void sendBootCompleted(IIntentReceiver resultTo) {
-        final boolean systemUserFinishedBooting;
-
         // Get a copy of mStartedUsers to use outside of lock
         SparseArray<UserState> startedUsers;
         synchronized (mLock) {
-            systemUserFinishedBooting = mCurrentUserId != UserHandle.USER_SYSTEM;
             startedUsers = mStartedUsers.clone();
         }
         for (int i = 0; i < startedUsers.size(); i++) {
             UserState uss = startedUsers.valueAt(i);
-            if (systemUserFinishedBooting && uss.mHandle.isSystem()) {
-                // On Automotive, at this point the system user has already been started and
-                // unlocked, and some of the tasks we do here have already been done. So skip those
-                // in that case.
-                // TODO(b/132262830): this workdound shouldn't be necessary once we move the
-                // headless-user start logic to UserManager-land
-                Slog.d(TAG, "sendBootCompleted(): skipping on non-current system user");
-                continue;
+            if (!UserManager.isHeadlessSystemUserMode()) {
+                finishUserBoot(uss, resultTo);
+            } else if (uss.mHandle.isSystem()) {
+                // In case of headless system user mode, send only locked boot complete broadcast
+                // for system user since finishUserBoot call will be made using other code path;
+                // for non-system user, do nothing since finishUserBoot will be called elsewhere.
+                sendLockedBootCompletedBroadcast(resultTo, uss.mHandle.getIdentifier());
+                return;
             }
-            finishUserBoot(uss, resultTo);
         }
     }
 
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 3e32380..2982545 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -137,6 +137,11 @@
     boolean readPersistentDataLocked() {
         mGameModes.clear();
 
+        if (!mSettingsFile.exists()) {
+            Slog.v(GameManagerService.TAG, "Settings file doesn't exists, skip reading");
+            return false;
+        }
+
         try {
             final FileInputStream str = mSettingsFile.openRead();
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6614e06..1122f7f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2098,26 +2098,28 @@
         ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
                 beginTimeMillis, endTimeMillis, flags);
         Objects.requireNonNull(callback, "callback cannot be null");
-
         ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
-        boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
-        boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
-        boolean isCallerPermissionController;
-        try {
-            isCallerPermissionController = pm.getPackageUid(
-                    mContext.getPackageManager().getPermissionControllerPackageName(), 0)
-                    == Binder.getCallingUid();
-        } catch (PackageManager.NameNotFoundException doesNotHappen) {
-            return;
-        }
+        boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
+        if (!isSelfRequest) {
+            boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
+            boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+            boolean isCallerPermissionController;
+            try {
+                isCallerPermissionController = pm.getPackageUid(
+                        mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+                        == Binder.getCallingUid();
+            } catch (PackageManager.NameNotFoundException doesNotHappen) {
+                return;
+            }
 
-        if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
-            mHandler.post(() -> callback.sendResult(new Bundle()));
-            return;
-        }
+            if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
+                mHandler.post(() -> callback.sendResult(new Bundle()));
+                return;
+            }
 
-        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
-                Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+            mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+                    Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+        }
 
         final String[] opNamesArray = (opNames != null)
                 ? opNames.toArray(new String[opNames.size()]) : null;
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index ed62abc..2b0157c 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -49,8 +49,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 
-import libcore.util.EmptyArray;
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -85,6 +83,8 @@
     private static final String TAG = DiscreteRegistry.class.getSimpleName();
 
     private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
+    private static final long TIMELINE_QUANTIZATION = Duration.ofMinutes(1).toMillis();
+
     private static final String TAG_HISTORY = "h";
     private static final String ATTR_VERSION = "v";
     private static final int CURRENT_VERSION = 1;
@@ -107,6 +107,8 @@
     private static final String ATTR_UID_STATE = "us";
     private static final String ATTR_FLAGS = "f";
 
+    private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+
     // Lock for read/write access to on disk state
     private final Object mOnDiskLock = new Object();
 
@@ -119,6 +121,9 @@
     @GuardedBy("mInMemoryLock")
     private DiscreteOps mDiscreteOps;
 
+    @GuardedBy("mOnDiskLock")
+    private DiscreteOps mCachedOps = null;
+
     DiscreteRegistry(Object inMemoryLock) {
         mInMemoryLock = inMemoryLock;
         mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
@@ -173,23 +178,25 @@
                     }
                 }
             }
-        }
-        DiscreteOps discreteOps;
-        synchronized (mInMemoryLock) {
-            discreteOps = mDiscreteOps;
-            mDiscreteOps = new DiscreteOps();
-        }
-        if (discreteOps.isEmpty()) {
-            return;
-        }
-        long currentTimeStamp = Instant.now().toEpochMilli();
-        try {
-            final File file = new File(mDiscreteAccessDir, currentTimeStamp + TIMELINE_FILE_SUFFIX);
-            discreteOps.writeToFile(file);
-        } catch (Throwable t) {
-            Slog.e(TAG,
-                    "Error writing timeline state: " + t.getMessage() + " "
-                            + Arrays.toString(t.getStackTrace()));
+            DiscreteOps discreteOps;
+            synchronized (mInMemoryLock) {
+                discreteOps = mDiscreteOps;
+                mDiscreteOps = new DiscreteOps();
+                mCachedOps = null;
+            }
+            if (discreteOps.isEmpty()) {
+                return;
+            }
+            long currentTimeStamp = Instant.now().toEpochMilli();
+            try {
+                final File file = new File(mDiscreteAccessDir,
+                        currentTimeStamp + TIMELINE_FILE_SUFFIX);
+                discreteOps.writeToFile(file);
+            } catch (Throwable t) {
+                Slog.e(TAG,
+                        "Error writing timeline state: " + t.getMessage() + " "
+                                + Arrays.toString(t.getStackTrace()));
+            }
         }
     }
 
@@ -197,25 +204,33 @@
             long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
             @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
             @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
-        writeAndClearAccessHistory();
-        DiscreteOps discreteOps = new DiscreteOps();
-        readDiscreteOpsFromDisk(discreteOps, beginTimeMillis, endTimeMillis, filter, uidFilter,
-                packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+        DiscreteOps discreteOps = getAndCacheDiscreteOps();
+        discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
+                opNamesFilter, attributionTagFilter, flagsFilter);
         discreteOps.applyToHistoricalOps(result);
         return;
     }
 
-    private void readDiscreteOpsFromDisk(DiscreteOps discreteOps, long beginTimeMillis,
-            long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
-            @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
-            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+    private DiscreteOps getAndCacheDiscreteOps() {
+        DiscreteOps discreteOps = new DiscreteOps();
+
         synchronized (mOnDiskLock) {
-            long historyBeginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
-                    ChronoUnit.MILLIS).toEpochMilli();
-            if (historyBeginTimeMillis > endTimeMillis) {
-                return;
+            synchronized (mInMemoryLock) {
+                discreteOps.merge(mDiscreteOps);
             }
-            beginTimeMillis = max(beginTimeMillis, historyBeginTimeMillis);
+            if (mCachedOps == null) {
+                mCachedOps = new DiscreteOps();
+                readDiscreteOpsFromDisk(mCachedOps);
+            }
+            discreteOps.merge(mCachedOps);
+        }
+        return discreteOps;
+    }
+
+    private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
+        synchronized (mOnDiskLock) {
+            long beginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+                    ChronoUnit.MILLIS).toEpochMilli();
 
             final File[] files = mDiscreteAccessDir.listFiles();
             if (files != null && files.length > 0) {
@@ -229,8 +244,7 @@
                     if (timestamp < beginTimeMillis) {
                         continue;
                     }
-                    discreteOps.readFromFile(f, beginTimeMillis, endTimeMillis, filter, uidFilter,
-                            packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+                    discreteOps.readFromFile(f, beginTimeMillis);
                 }
             }
         }
@@ -251,15 +265,11 @@
             @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
             @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
             int nDiscreteOps) {
-        DiscreteOps discreteOps = new DiscreteOps();
-        synchronized (mOnDiskLock) {
-            writeAndClearAccessHistory();
-            String[] opNamesFilter = dumpOp == OP_NONE ? EmptyArray.STRING
-                    : new String[]{AppOpsManager.opToPublicName(dumpOp)};
-            readDiscreteOpsFromDisk(discreteOps, 0, Instant.now().toEpochMilli(), filter,
-                    uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
-                    OP_FLAGS_ALL);
-        }
+        DiscreteOps discreteOps = getAndCacheDiscreteOps();
+        String[] opNamesFilter = dumpOp == OP_NONE ? null
+                : new String[]{AppOpsManager.opToPublicName(dumpOp)};
+        discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
+                opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
         discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
     }
 
@@ -270,7 +280,7 @@
         if (!isDiscreteUid(uid)) {
             return false;
         }
-        if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) == 0) {
+        if ((flags & (OP_FLAGS_DISCRETE)) == 0) {
             return false;
         }
         return true;
@@ -298,6 +308,19 @@
             mUids = new ArrayMap<>();
         }
 
+        boolean isEmpty() {
+            return mUids.isEmpty();
+        }
+
+        void merge(DiscreteOps other) {
+            int nUids = other.mUids.size();
+            for (int i = 0; i < nUids; i++) {
+                int uid = other.mUids.keyAt(i);
+                DiscreteUidOps uidOps = other.mUids.valueAt(i);
+                getOrCreateDiscreteUidOps(uid).merge(uidOps);
+            }
+        }
+
         void addDiscreteAccess(int op, int uid, @NonNull String packageName,
                 @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
                 @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
@@ -305,6 +328,25 @@
                     uidState, accessTime, accessDuration);
         }
 
+        private void filter(long beginTimeMillis, long endTimeMillis,
+                @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+                @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+            if ((filter & FILTER_BY_UID) != 0) {
+                ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
+                uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
+                mUids = uids;
+            }
+            int nUids = mUids.size();
+            for (int i = nUids - 1; i >= 0; i--) {
+                mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
+                        opNamesFilter, attributionTagFilter, flagsFilter);
+                if (mUids.valueAt(i).isEmpty()) {
+                    mUids.removeAt(i);
+                }
+            }
+        }
+
         private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
             int nUids = mUids.size();
             for (int i = 0; i < nUids; i++) {
@@ -353,14 +395,7 @@
             return result;
         }
 
-        boolean isEmpty() {
-            return mUids.isEmpty();
-        }
-
-        private void readFromFile(File f, long beginTimeMillis, long endTimeMillis,
-                @AppOpsManager.HistoricalOpsRequestFilter int filter,
-                int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
-                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+        private void readFromFile(File f, long beginTimeMillis) {
             try {
                 FileInputStream stream = new FileInputStream(f);
                 TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -377,12 +412,7 @@
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_UID.equals(parser.getName())) {
                         int uid = parser.getAttributeInt(null, ATTR_UID, -1);
-                        if ((filter & FILTER_BY_UID) != 0 && uid != uidFilter) {
-                            continue;
-                        }
-                        getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis,
-                                endTimeMillis, filter, packageNameFilter, opNamesFilter,
-                                attributionTagFilter, flagsFilter);
+                        getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis);
                     }
                 }
             } catch (Throwable t) {
@@ -400,6 +430,38 @@
             mPackages = new ArrayMap<>();
         }
 
+        boolean isEmpty() {
+            return mPackages.isEmpty();
+        }
+
+        void merge(DiscreteUidOps other) {
+            int nPackages = other.mPackages.size();
+            for (int i = 0; i < nPackages; i++) {
+                String packageName = other.mPackages.keyAt(i);
+                DiscretePackageOps p = other.mPackages.valueAt(i);
+                getOrCreateDiscretePackageOps(packageName).merge(p);
+            }
+        }
+
+        private void filter(long beginTimeMillis, long endTimeMillis,
+                @AppOpsManager.HistoricalOpsRequestFilter int filter,
+                @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+            if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
+                ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
+                packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
+                mPackages = packages;
+            }
+            int nPackages = mPackages.size();
+            for (int i = nPackages - 1; i >= 0; i--) {
+                mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
+                        attributionTagFilter, flagsFilter);
+                if (mPackages.valueAt(i).isEmpty()) {
+                    mPackages.removeAt(i);
+                }
+            }
+        }
+
         void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
                 @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                 long accessTime, long accessDuration) {
@@ -445,22 +507,12 @@
             }
         }
 
-        void deserialize(TypedXmlPullParser parser, long beginTimeMillis,
-                long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter,
-                @Nullable String packageNameFilter,
-                @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
-                @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
             int depth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_PACKAGE.equals(parser.getName())) {
                     String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                    if ((filter & FILTER_BY_PACKAGE_NAME) != 0
-                            && !packageName.equals(packageNameFilter)) {
-                        continue;
-                    }
-                    getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis,
-                            endTimeMillis, filter, opNamesFilter, attributionTagFilter,
-                            flagsFilter);
+                    getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis);
                 }
             }
         }
@@ -473,6 +525,10 @@
             mPackageOps = new ArrayMap<>();
         }
 
+        boolean isEmpty() {
+            return mPackageOps.isEmpty();
+        }
+
         void addDiscreteAccess(int op, @Nullable String attributionTag,
                 @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                 long accessTime, long accessDuration) {
@@ -480,6 +536,35 @@
                     accessDuration);
         }
 
+        void merge(DiscretePackageOps other) {
+            int nOps = other.mPackageOps.size();
+            for (int i = 0; i < nOps; i++) {
+                int opId = other.mPackageOps.keyAt(i);
+                DiscreteOp op = other.mPackageOps.valueAt(i);
+                getOrCreateDiscreteOp(opId).merge(op);
+            }
+        }
+
+        private void filter(long beginTimeMillis, long endTimeMillis,
+                @AppOpsManager.HistoricalOpsRequestFilter int filter,
+                @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
+                @AppOpsManager.OpFlags int flagsFilter) {
+            int nOps = mPackageOps.size();
+            for (int i = nOps - 1; i >= 0; i--) {
+                int opId = mPackageOps.keyAt(i);
+                if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
+                        AppOpsManager.opToPublicName(opId))) {
+                    mPackageOps.removeAt(i);
+                    continue;
+                }
+                mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
+                        attributionTagFilter, flagsFilter);
+                if (mPackageOps.valueAt(i).isEmpty()) {
+                    mPackageOps.removeAt(i);
+                }
+            }
+        }
+
         private DiscreteOp getOrCreateDiscreteOp(int op) {
             DiscreteOp result = mPackageOps.get(op);
             if (result == null) {
@@ -519,20 +604,12 @@
             }
         }
 
-        void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
-                @AppOpsManager.HistoricalOpsRequestFilter int filter,
-                @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
-                @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
             int depth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_OP.equals(parser.getName())) {
                     int op = parser.getAttributeInt(null, ATTR_OP_ID);
-                    if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
-                            AppOpsManager.opToPublicName(op))) {
-                        continue;
-                    }
-                    getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis, endTimeMillis,
-                            filter, attributionTagFilter, flagsFilter);
+                    getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis);
                 }
             }
         }
@@ -545,31 +622,66 @@
             mAttributedOps = new ArrayMap<>();
         }
 
+        boolean isEmpty() {
+            return mAttributedOps.isEmpty();
+        }
+
+        void merge(DiscreteOp other) {
+            int nTags = other.mAttributedOps.size();
+            for (int i = 0; i < nTags; i++) {
+                String tag = other.mAttributedOps.keyAt(i);
+                List<DiscreteOpEvent> otherEvents = other.mAttributedOps.valueAt(i);
+                List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(tag);
+                mAttributedOps.put(tag, stableListMerge(events, otherEvents));
+            }
+        }
+
+        private void filter(long beginTimeMillis, long endTimeMillis,
+                @AppOpsManager.HistoricalOpsRequestFilter int filter,
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+            if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
+                ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
+                attributedOps.put(attributionTagFilter,
+                        getOrCreateDiscreteOpEventsList(attributionTagFilter));
+                mAttributedOps = attributedOps;
+            }
+
+            int nTags = mAttributedOps.size();
+            for (int i = nTags - 1; i >= 0; i--) {
+                String tag = mAttributedOps.keyAt(i);
+                List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
+                list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+                mAttributedOps.put(tag, list);
+                if (list.size() == 0) {
+                    mAttributedOps.removeAt(i);
+                }
+            }
+        }
+
         void addDiscreteAccess(@Nullable String attributionTag,
                 @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                 long accessTime, long accessDuration) {
             List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
                     attributionTag);
-            accessTime = Instant.ofEpochMilli(accessTime).truncatedTo(
-                    ChronoUnit.MINUTES).toEpochMilli();
+            accessTime = accessTime / TIMELINE_QUANTIZATION * TIMELINE_QUANTIZATION;
 
             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;
-                }
+            int i = nAttributedOps;
+            for (; i > 0; i--) {
+                DiscreteOpEvent previousOp = attributedOps.get(i - 1);
                 if (previousOp.mNoteTime < accessTime) {
                     break;
                 }
                 if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
-                    return;
+                    if (accessDuration != previousOp.mNoteDuration
+                            && accessDuration > TIMELINE_QUANTIZATION) {
+                        break;
+                    } else {
+                        return;
+                    }
                 }
             }
-            attributedOps.add(new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+            attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
         }
 
         private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -633,18 +745,11 @@
             }
         }
 
-        void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
-                @AppOpsManager.HistoricalOpsRequestFilter int filter,
-                @Nullable String attributionTagFilter,
-                @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
             int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 if (TAG_TAG.equals(parser.getName())) {
                     String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
-                    if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !attributionTag.equals(
-                            attributionTagFilter)) {
-                        continue;
-                    }
                     List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
                             attributionTag);
                     int innerDepth = parser.getDepth();
@@ -655,11 +760,7 @@
                                     -1);
                             int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
                             int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
-                            if ((flagsFilter & opFlags) == 0) {
-                                continue;
-                            }
-                            if ((noteTime + noteDuration < beginTimeMillis
-                                    && noteTime > endTimeMillis)) {
+                            if (noteTime + noteDuration < beginTimeMillis) {
                                 continue;
                             }
                             DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
@@ -715,5 +816,41 @@
             out.attributeInt(null, ATTR_FLAGS, mOpFlag);
         }
     }
+
+    private static List<DiscreteOpEvent> stableListMerge(List<DiscreteOpEvent> a,
+            List<DiscreteOpEvent> b) {
+        int nA = a.size();
+        int nB = b.size();
+        int i = 0;
+        int k = 0;
+        List<DiscreteOpEvent> result = new ArrayList<>(nA + nB);
+        while (i < nA || k < nB) {
+            if (i == nA) {
+                result.add(b.get(k++));
+            } else if (k == nB) {
+                result.add(a.get(i++));
+            } else if (a.get(i).mNoteTime < b.get(k).mNoteTime) {
+                result.add(a.get(i++));
+            } else {
+                result.add(b.get(k++));
+            }
+        }
+        return result;
+    }
+
+    private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
+            long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+        int n = list.size();
+        List<DiscreteOpEvent> result = new ArrayList<>(n);
+        for (int i = 0; i < n; i++) {
+            DiscreteOpEvent event = list.get(i);
+            if ((event.mOpFlag & flagsFilter) != 0
+                    && event.mNoteTime + event.mNoteDuration > beginTimeMillis
+                    && event.mNoteTime < endTimeMillis) {
+                result.add(event);
+            }
+        }
+        return result;
+    }
 }
 
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 22d628b..4435c47 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -532,7 +532,7 @@
                         System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
                         attributionTag, uidState, flags, increment);
                 mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
-                        flags, uidState, increment, eventStartTime);
+                        flags, uidState, eventStartTime, increment);
             }
         }
     }
@@ -795,7 +795,7 @@
     private static boolean isApiEnabled() {
         return Binder.getCallingUid() == Process.myUid()
                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+                PROPERTY_PERMISSIONS_HUB_ENABLED, true);
     }
 
     private static final class Persistence {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5020917..5bf15dc 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -43,6 +44,8 @@
 import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IUserManager;
 import android.os.Parcel;
@@ -55,9 +58,15 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.autofill.AutofillManagerInternal;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
 import android.widget.Toast;
 
 import com.android.internal.R;
@@ -169,6 +178,8 @@
     // DeviceConfig properties
     private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
     private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+    private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
+    private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
 
     private final ActivityManagerInternal mAmInternal;
     private final IUriGrantsManager mUgm;
@@ -179,8 +190,10 @@
     private final AppOpsManager mAppOps;
     private final ContentCaptureManagerInternal mContentCaptureInternal;
     private final AutofillManagerInternal mAutofillInternal;
+    private final TextClassificationManager mTextClassificationManager;
     private final IBinder mPermissionOwner;
     private final HostClipboardMonitor mHostClipboardMonitor;
+    private final Handler mWorkerHandler;
 
     @GuardedBy("mLock")
     private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@@ -188,6 +201,9 @@
     @GuardedBy("mLock")
     private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
 
+    @GuardedBy("mLock")
+    private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
+
     private final Object mLock = new Object();
 
     /**
@@ -205,6 +221,8 @@
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);
         mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);
+        mTextClassificationManager = (TextClassificationManager)
+                getContext().getSystemService(Context.TEXT_CLASSIFICATION_SERVICE);
         final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
         mPermissionOwner = permOwner;
         if (IS_EMULATOR) {
@@ -231,6 +249,10 @@
         updateConfig();
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,
                 getContext().getMainExecutor(), properties -> updateConfig());
+
+        HandlerThread workerThread = new HandlerThread(TAG);
+        workerThread.start();
+        mWorkerHandler = workerThread.getThreadHandler();
     }
 
     @Override
@@ -249,6 +271,8 @@
         synchronized (mLock) {
             mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
                     PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+            mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
+                    PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
         }
     }
 
@@ -274,6 +298,9 @@
         /** Package of the app that set {@link #primaryClip}. */
         String mPrimaryClipPackage;
 
+        /** Uids that have already triggered a toast notification for {@link #primaryClip} */
+        final SparseBooleanArray mNotifiedUids = new SparseBooleanArray();
+
         final HashSet<String> activePermissionOwners
                 = new HashSet<String>();
 
@@ -588,6 +615,10 @@
             }
         }
 
+        if (clip != null) {
+            startClassificationLocked(clip);
+        }
+
         // Update this user
         final int userId = UserHandle.getUserId(uid);
         setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -649,6 +680,7 @@
             return;
         }
         clipboard.primaryClip = clip;
+        clipboard.mNotifiedUids.clear();
         if (clip != null) {
             clipboard.primaryClipUid = uid;
             clipboard.mPrimaryClipPackage = sourcePackage;
@@ -686,6 +718,68 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void startClassificationLocked(@NonNull ClipData clip) {
+        TextClassifier classifier;
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            classifier = mTextClassificationManager.createTextClassificationSession(
+                    new TextClassificationContext.Builder(
+                            getContext().getPackageName(),
+                            TextClassifier.WIDGET_TYPE_CLIPBOARD
+                    ).build()
+            );
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        if (clip.getItemCount() == 0) {
+            clip.getDescription().setClassificationStatus(
+                    ClipDescription.CLASSIFICATION_NOT_PERFORMED);
+            return;
+        }
+        CharSequence text = clip.getItemAt(0).getText();
+        if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength
+                || text.length() > classifier.getMaxGenerateLinksTextLength()) {
+            clip.getDescription().setClassificationStatus(
+                    ClipDescription.CLASSIFICATION_NOT_PERFORMED);
+            return;
+        }
+
+        mWorkerHandler.post(() -> doClassification(text, clip, classifier));
+    }
+
+    @WorkerThread
+    private void doClassification(
+            CharSequence text, ClipData clip, TextClassifier classifier) {
+        TextLinks.Request request = new TextLinks.Request.Builder(text).build();
+        TextLinks links;
+        try {
+            links = classifier.generateLinks(request);
+        } finally {
+            classifier.destroy();
+        }
+
+        // Find the highest confidence for each entity in the text.
+        ArrayMap<String, Float> confidences = new ArrayMap<>();
+        for (TextLinks.TextLink link : links.getLinks()) {
+            for (int i = 0; i < link.getEntityCount(); i++) {
+                String entity = link.getEntity(i);
+                float conf = link.getConfidenceScore(entity);
+                if (conf > confidences.getOrDefault(entity, 0f)) {
+                    confidences.put(entity, conf);
+                }
+            }
+        }
+
+        synchronized (mLock) {
+            clip.getDescription().setConfidenceScores(confidences);
+            if (!links.getLinks().isEmpty()) {
+                clip.getItemAt(0).setTextLinks(links);
+            }
+        }
+    }
+
     private boolean isDeviceLocked(@UserIdInt int userId) {
         final long token = Binder.clearCallingIdentity();
         try {
@@ -917,6 +1011,10 @@
         if (!mShowAccessNotifications) {
             return;
         }
+        if (Settings.Secure.getInt(getContext().getContentResolver(),
+                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) == 0) {
+            return;
+        }
         // Don't notify if the app accessing the clipboard is the same as the current owner.
         if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
             return;
@@ -939,35 +1037,42 @@
                 && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
             return;
         }
+        // Don't notify if already notified for this uid and clip.
+        if (clipboard.mNotifiedUids.get(uid)) {
+            return;
+        }
+        clipboard.mNotifiedUids.put(uid, true);
 
-        // Retrieve the app label of the source of the clip data
-        CharSequence sourceAppLabel = null;
-        if (clipboard.mPrimaryClipPackage != null) {
+        Binder.withCleanCallingIdentity(() -> {
+            // Retrieve the app label of the source of the clip data
+            CharSequence sourceAppLabel = null;
+            if (clipboard.mPrimaryClipPackage != null) {
+                try {
+                    sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
+                            clipboard.mPrimaryClipPackage, 0, userId));
+                } catch (PackageManager.NameNotFoundException e) {
+                    // leave label as null
+                }
+            }
+
             try {
-                sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
-                        clipboard.mPrimaryClipPackage, 0, userId));
+                CharSequence callingAppLabel = mPm.getApplicationLabel(
+                        mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
+                String message;
+                if (sourceAppLabel != null) {
+                    message = getContext().getString(
+                            R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
+                } else {
+                    message = getContext().getString(
+                            R.string.pasted_from_clipboard, callingAppLabel);
+                }
+                Slog.i(TAG, message);
+                Toast.makeText(
+                        getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT)
+                        .show();
             } catch (PackageManager.NameNotFoundException e) {
-                // leave label as null
+                // do nothing
             }
-        }
-
-        try {
-            CharSequence callingAppLabel = mPm.getApplicationLabel(
-                    mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
-            String message;
-            if (sourceAppLabel != null) {
-                message = getContext().getString(
-                        R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
-            } else {
-                message = getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
-            }
-            Slog.i(TAG, message);
-            Binder.withCleanCallingIdentity(() ->
-                    Toast.makeText(getContext(), UiThread.get().getLooper(), message,
-                            Toast.LENGTH_SHORT)
-                            .show());
-        } catch (PackageManager.NameNotFoundException e) {
-            // do nothing
-        }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 5cf478a..ae9b001 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -28,6 +28,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 
+import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.CompatibilityChangeInfo;
 import com.android.internal.compat.OverrideAllowedState;
 import com.android.server.compat.config.Change;
@@ -55,7 +56,7 @@
      * A change ID to be used only in the CTS test for this SystemApi
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion.
+    @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion.
     static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
 
     /**
@@ -233,7 +234,7 @@
      * @param app Info about the app in question
      * @return {@code true} if the change should be enabled for the package.
      */
-    boolean isEnabled(ApplicationInfo app) {
+    boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) {
         if (app == null) {
             return defaultValue();
         }
@@ -244,7 +245,13 @@
             return false;
         }
         if (getEnableSinceTargetSdk() != -1) {
-            return app.targetSdkVersion >= getEnableSinceTargetSdk();
+            // If the change is gated by a platform version newer than the one currently installed
+            // on the device, disregard the app's target sdk version.
+            int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk());
+            if (compareSdk != app.targetSdkVersion) {
+                compareSdk = app.targetSdkVersion;
+            }
+            return compareSdk >= getEnableSinceTargetSdk();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 2c053b4..ef86f42 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -74,12 +74,14 @@
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
     private final OverrideValidatorImpl mOverrideValidator;
+    private final AndroidBuildClassifier mAndroidBuildClassifier;
     private Context mContext;
     private File mOverridesFile;
 
     @VisibleForTesting
     CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
         mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
+        mAndroidBuildClassifier = androidBuildClassifier;
         mContext = context;
     }
 
@@ -133,7 +135,7 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange c = mChanges.valueAt(i);
-                if (!c.isEnabled(app)) {
+                if (!c.isEnabled(app, mAndroidBuildClassifier)) {
                     disabled.add(c.getId());
                 }
             }
@@ -175,7 +177,7 @@
                 // we know nothing about this change: default behaviour is enabled.
                 return true;
             }
-            return c.isEnabled(app);
+            return c.isEnabled(app, mAndroidBuildClassifier);
         }
     }
 
@@ -475,7 +477,7 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange c = mChanges.valueAt(i);
-                if (c.isEnabled(applicationInfo)) {
+                if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
                     enabled.add(c.getId());
                 } else {
                     disabled.add(c.getId());
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index fe5b4a9..aa66a1a 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -22,6 +22,7 @@
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -85,6 +86,9 @@
         if (debuggableBuild) {
             return new OverrideAllowedState(ALLOWED, -1, -1);
         }
+        if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
+            return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
+        }
         PackageManager packageManager = mContext.getPackageManager();
         if (packageManager == null) {
             throw new IllegalStateException("No PackageManager!");
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index d17753f..2be39aa 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -66,18 +66,22 @@
     private final Context mContext;
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
+    private final AndroidBuildClassifier mBuildClassifier;
 
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
-        mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+        mBuildClassifier = new AndroidBuildClassifier();
+        mCompatConfig = CompatConfig.create(mBuildClassifier, mContext);
     }
 
     @VisibleForTesting
-    PlatformCompat(Context context, CompatConfig compatConfig) {
+    PlatformCompat(Context context, CompatConfig compatConfig,
+                   AndroidBuildClassifier buildClassifier) {
         mContext = context;
         mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = compatConfig;
+        mBuildClassifier = buildClassifier;
 
         registerPackageReceiver(context);
     }
@@ -392,7 +396,8 @@
             return false;
         }
         if (change.getEnableSinceTargetSdk() > 0) {
-            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
+            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q
+                && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityResources.java b/services/core/java/com/android/server/connectivity/ConnectivityResources.java
new file mode 100644
index 0000000..45cf21e
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ConnectivityResources.java
@@ -0,0 +1,83 @@
+/*
+ * 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.connectivity;
+
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.server.ConnectivityService;
+
+import java.util.List;
+
+/**
+ * Utility to obtain the {@link ConnectivityService} {@link Resources}, in the
+ * ServiceConnectivityResources APK.
+ */
+public class ConnectivityResources {
+    private static final String RESOURCES_APK_INTENT =
+            "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK";
+    private static final String RES_PKG_DIR = "/apex/com.android.tethering/";
+
+    @NonNull
+    private final Context mContext;
+
+    @Nullable
+    private Resources mResources = null;
+
+    public ConnectivityResources(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Get the {@link Resources} of the ServiceConnectivityResources APK.
+     */
+    public synchronized Resources get() {
+        if (mResources != null) {
+            return mResources;
+        }
+
+        final List<ResolveInfo> pkgs = mContext.getPackageManager()
+                .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY);
+        pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR));
+        if (pkgs.size() > 1) {
+            Log.wtf(ConnectivityResources.class.getSimpleName(),
+                    "More than one package found: " + pkgs);
+        }
+        if (pkgs.isEmpty()) {
+            throw new IllegalStateException("No connectivity resource package found");
+        }
+
+        final Context pkgContext;
+        try {
+            pkgContext = mContext.createPackageContext(
+                    pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Resolved package not found", e);
+        }
+
+        mResources = pkgContext.getResources();
+        return mResources;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 4f6b530..7a5abf8 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
@@ -33,6 +32,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
 import android.net.IDnsResolver;
 import android.net.InetAddresses;
 import android.net.LinkProperties;
@@ -128,7 +128,7 @@
     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
 
     public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
-        final String mode = getPrivateDnsMode(cr);
+        final String mode = ConnectivityManager.getPrivateDnsMode(cr);
 
         final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
 
@@ -479,13 +479,6 @@
         return result;
     }
 
-    private static String getPrivateDnsMode(ContentResolver cr) {
-        String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
-        if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
-        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
-        return mode;
-    }
-
     private static String getStringSetting(ContentResolver cr, String which) {
         return Settings.Global.getString(cr, which);
     }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index fa80b25..c66a280 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static com.android.net.module.util.CollectionUtils.contains;
 
 import android.annotation.NonNull;
@@ -35,6 +37,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.NetworkStackConstants;
+import com.android.server.ConnectivityService;
 
 import java.net.Inet6Address;
 import java.util.Objects;
@@ -94,12 +97,15 @@
     private Inet6Address mIPv6Address;
     private State mState = State.IDLE;
 
+    private boolean mEnableClatOnCellular;
     private boolean mPrefixDiscoveryRunning;
 
-    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver) {
+    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
+            ConnectivityService.Dependencies deps) {
         mDnsResolver = dnsResolver;
         mNetd = netd;
         mNetwork = nai;
+        mEnableClatOnCellular = deps.getCellular464XlatEnabled();
     }
 
     /**
@@ -111,7 +117,7 @@
      * @return true if the network requires clat, false otherwise.
      */
     @VisibleForTesting
-    protected static boolean requiresClat(NetworkAgentInfo nai) {
+    protected boolean requiresClat(NetworkAgentInfo nai) {
         // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
         final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType());
         final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState());
@@ -126,7 +132,9 @@
         final boolean skip464xlat = (nai.netAgentConfig() != null)
                 && nai.netAgentConfig().skip464xlat;
 
-        return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
+        return supported && connected && isIpv6OnlyNetwork && !skip464xlat
+            && (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
+                ? isCellular464XlatEnabled() : true);
     }
 
     /**
@@ -137,7 +145,7 @@
      * @return true if the network should start clat, false otherwise.
      */
     @VisibleForTesting
-    protected static boolean shouldStartClat(NetworkAgentInfo nai) {
+    protected boolean shouldStartClat(NetworkAgentInfo nai) {
         LinkProperties lp = nai.linkProperties;
         return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
     }
@@ -507,4 +515,9 @@
     protected int getNetId() {
         return mNetwork.network.getNetId();
     }
+
+    @VisibleForTesting
+    protected boolean isCellular464XlatEnabled() {
+        return mEnableClatOnCellular;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1d0e115..803cc9d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -341,7 +341,7 @@
             @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
-            QosCallbackTracker qosCallbackTracker) {
+            QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
         Objects.requireNonNull(net);
         Objects.requireNonNull(info);
         Objects.requireNonNull(lp);
@@ -355,7 +355,7 @@
         linkProperties = lp;
         networkCapabilities = nc;
         mScore = score;
-        clatd = new Nat464Xlat(this, netd, dnsResolver);
+        clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
         mConnService = connService;
         mContext = context;
         mHandler = handler;
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyService.java
similarity index 75%
rename from services/core/java/com/android/server/connectivity/PacProxyInstaller.java
rename to services/core/java/com/android/server/connectivity/PacProxyService.java
index aadaf4d..d23b488 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -26,12 +28,16 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.net.IPacProxyInstalledListener;
+import android.net.IPacProxyManager;
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -44,6 +50,7 @@
 import com.android.net.IProxyCallback;
 import com.android.net.IProxyPortListener;
 import com.android.net.IProxyService;
+import com.android.net.module.util.PermissionUtils;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -53,7 +60,7 @@
 /**
  * @hide
  */
-public class PacProxyInstaller {
+public class PacProxyService extends IPacProxyManager.Stub {
     private static final String PAC_PACKAGE = "com.android.pacprocessor";
     private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
     private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
@@ -61,7 +68,7 @@
     private static final String PROXY_PACKAGE = "com.android.proxyhandler";
     private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
 
-    private static final String TAG = "PacProxyInstaller";
+    private static final String TAG = "PacProxyService";
 
     private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
 
@@ -71,10 +78,6 @@
     private static final int DELAY_LONG = 4;
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
-    // Return values for #setCurrentProxyScriptUrl
-    public static final boolean DONT_SEND_BROADCAST = false;
-    public static final boolean DO_SEND_BROADCAST = true;
-
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
     private volatile Uri mPacUrl = Uri.EMPTY;
@@ -93,8 +96,8 @@
     private volatile boolean mHasSentBroadcast;
     private volatile boolean mHasDownloaded;
 
-    private Handler mConnectivityHandler;
-    private final int mProxyMessage;
+    private final RemoteCallbackList<IPacProxyInstalledListener>
+            mCallbacks = new RemoteCallbackList<>();
 
     /**
      * Used for locking when setting mProxyService and all references to mCurrentPac.
@@ -102,6 +105,13 @@
     private final Object mProxyLock = new Object();
 
     /**
+     * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
+     * last URL and port, and the broadcast message being sent with the correct arguments.
+     * TODO : this should probably protect all instances of these variables
+     */
+    private final Object mBroadcastStateLock = new Object();
+
+    /**
      * Runnable to download PAC script.
      * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
      * latest data fetched from mPacUrl is stored in mProxyService.
@@ -146,10 +156,10 @@
         }
     }
 
-    public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
+    public PacProxyService(@NonNull Context context) {
         mContext = context;
         mLastPort = -1;
-        final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
+        final HandlerThread netThread = new HandlerThread("android.pacproxyservice",
                 android.os.Process.THREAD_PRIORITY_DEFAULT);
         netThread.start();
         mNetThreadHandler = new Handler(netThread.getLooper());
@@ -158,8 +168,6 @@
                 context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
         context.registerReceiver(new PacRefreshIntentReceiver(),
                 new IntentFilter(ACTION_PAC_REFRESH));
-        mConnectivityHandler = handler;
-        mProxyMessage = proxyMessage;
     }
 
     private AlarmManager getAlarmManager() {
@@ -169,38 +177,52 @@
         return mAlarmManager;
     }
 
+    @Override
+    public void addListener(IPacProxyInstalledListener listener) {
+        PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS);
+        mCallbacks.register(listener);
+    }
+
+    @Override
+    public void removeListener(IPacProxyInstalledListener listener) {
+        PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS);
+        mCallbacks.unregister(listener);
+    }
+
     /**
      * Updates the PAC Proxy Installer with current Proxy information. This is called by
-     * the ProxyTracker directly before a broadcast takes place to allow
-     * the PacProxyInstaller to indicate that the broadcast should not be sent and the
-     * PacProxyInstaller will trigger a new broadcast when it is ready.
+     * the ProxyTracker through PacProxyManager before a broadcast takes place to allow
+     * the PacProxyService to indicate that the broadcast should not be sent and the
+     * PacProxyService will trigger a new broadcast when it is ready.
      *
      * @param proxy Proxy information that is about to be broadcast.
-     * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
-        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
-            if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
-                // Allow to send broadcast, nothing to do.
-                return DO_SEND_BROADCAST;
-            }
-            mPacUrl = proxy.getPacFileUrl();
-            mCurrentDelay = DELAY_1;
-            mHasSentBroadcast = false;
-            mHasDownloaded = false;
-            getAlarmManager().cancel(mPacRefreshIntent);
-            bind();
-            return DONT_SEND_BROADCAST;
-        } else {
-            getAlarmManager().cancel(mPacRefreshIntent);
-            synchronized (mProxyLock) {
-                mPacUrl = Uri.EMPTY;
-                mCurrentPac = null;
-                if (mProxyService != null) {
-                    unbind();
+    @Override
+    public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+        PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS);
+
+        synchronized (mBroadcastStateLock) {
+            if (proxy != null && !Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+                if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
+                mPacUrl = proxy.getPacFileUrl();
+                mCurrentDelay = DELAY_1;
+                mHasSentBroadcast = false;
+                mHasDownloaded = false;
+                getAlarmManager().cancel(mPacRefreshIntent);
+                bind();
+            } else {
+                getAlarmManager().cancel(mPacRefreshIntent);
+                synchronized (mProxyLock) {
+                    mPacUrl = Uri.EMPTY;
+                    mCurrentPac = null;
+                    if (mProxyService != null) {
+                        unbind();
+                    }
                 }
             }
-            return DO_SEND_BROADCAST;
         }
     }
 
@@ -275,6 +297,7 @@
         getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
     }
 
+    @GuardedBy("mProxyLock")
     private void setCurrentProxyScript(String script) {
         if (mProxyService == null) {
             Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -347,6 +370,9 @@
                             public void setProxyPort(int port) {
                                 if (mLastPort != -1) {
                                     // Always need to send if port changed
+                                    // TODO: Here lacks synchronization because this write cannot
+                                    // guarantee that it's visible from sendProxyIfNeeded() when
+                                    // it's called by a Runnable which is post by mNetThread.
                                     mHasSentBroadcast = false;
                                 }
                                 mLastPort = port;
@@ -365,8 +391,9 @@
                 }
             }
         };
-        mContext.bindService(intent, mProxyConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
+        mContext.bindService(intent,
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+                new HandlerExecutor(mNetThreadHandler), mProxyConnection);
     }
 
     private void unbind() {
@@ -383,16 +410,28 @@
     }
 
     private void sendPacBroadcast(ProxyInfo proxy) {
-        mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
+        final int length = mCallbacks.beginBroadcast();
+        for (int i = 0; i < length; i++) {
+            final IPacProxyInstalledListener listener = mCallbacks.getBroadcastItem(i);
+            if (listener != null) {
+                try {
+                    listener.onPacProxyInstalled(null /* network */, proxy);
+                } catch (RemoteException ignored) { }
+            }
+        }
+        mCallbacks.finishBroadcast();
     }
 
-    private synchronized void sendProxyIfNeeded() {
-        if (!mHasDownloaded || (mLastPort == -1)) {
-            return;
-        }
-        if (!mHasSentBroadcast) {
-            sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
-            mHasSentBroadcast = true;
+    // This method must be called on mNetThreadHandler.
+    private void sendProxyIfNeeded() {
+        synchronized (mBroadcastStateLock) {
+            if (!mHasDownloaded || (mLastPort == -1)) {
+                return;
+            }
+            if (!mHasSentBroadcast) {
+                sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+                mHasSentBroadcast = true;
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 9411e33..488677a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -31,14 +31,17 @@
 import static com.android.net.module.util.CollectionUtils.toIntArray;
 
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -54,7 +57,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.CollectionUtils;
-import com.android.server.LocalServices;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -71,7 +73,7 @@
  *
  * @hide
  */
-public class PermissionMonitor implements PackageManagerInternal.PackageListObserver {
+public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
     protected static final Boolean SYSTEM = Boolean.TRUE;
@@ -83,6 +85,7 @@
     private final SystemConfigManager mSystemConfigManager;
     private final INetd mNetd;
     private final Dependencies mDeps;
+    private final Context mContext;
 
     @GuardedBy("this")
     private final Set<UserHandle> mUsers = new HashSet<>();
@@ -102,6 +105,25 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
+
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
+            } else {
+                Log.wtf(TAG, "received unexpected intent: " + action);
+            }
+        }
+    };
+
     /**
      * Dependencies of PermissionMonitor, for injection in tests.
      */
@@ -127,6 +149,7 @@
         mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
         mNetd = netd;
         mDeps = deps;
+        mContext = context;
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -134,12 +157,14 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        if (pmi != null) {
-            pmi.getPackageList(this);
-        } else {
-            loge("failed to get the PackageManagerInternal service");
-        }
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver(
+                mIntentReceiver, intentFilter, null /* broadcastPermission */,
+                null /* scheduler */);
+
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
                 | MATCH_ANY_USER);
         if (apps == null) {
@@ -347,9 +372,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
@@ -384,9 +410,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If the newly-removed package falls within some VPN's uid range, update Netd with it.
         // This needs to happen before the mApps update below, since removeBypassingUids() depends
@@ -432,19 +459,6 @@
         }
     }
 
-    /**
-     * Called when a package is changed.
-     *
-     * @param packageName The name of the changed package.
-     * @param uid The uid of the changed package.
-     *
-     * @hide
-     */
-    @Override
-    public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
-    }
-
     private static int getNetdPermissionMask(String[] requestedPermissions,
                                              int[] requestedPermissionsFlags) {
         int permissions = 0;
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index d83ff83..8b9c836 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -27,15 +27,19 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Network;
+import android.net.PacProxyManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.net.module.util.ProxyUtils;
@@ -67,7 +71,7 @@
     // is not set. Individual networks have their own settings that override this. This member
     // is set through setDefaultProxy, which is called when the default network changes proxies
     // in its LinkProperties, or when ConnectivityService switches to a new default network, or
-    // when PacProxyInstaller resolves the proxy.
+    // when PacProxyService resolves the proxy.
     @Nullable
     @GuardedBy("mProxyLock")
     private volatile ProxyInfo mDefaultProxy = null;
@@ -77,16 +81,31 @@
 
     private final Handler mConnectivityServiceHandler;
 
-    // The object responsible for Proxy Auto Configuration (PAC).
-    @NonNull
-    private final PacProxyInstaller mPacProxyInstaller;
+    private final PacProxyManager mPacProxyManager;
+
+    private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
+        private final int mEvent;
+
+        PacProxyInstalledListener(int event) {
+            mEvent = event;
+        }
+
+        public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
+            mConnectivityServiceHandler
+                    .sendMessage(mConnectivityServiceHandler
+                    .obtainMessage(mEvent, new Pair<>(network, proxy)));
+        }
+    }
 
     public ProxyTracker(@NonNull final Context context,
             @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
         mContext = context;
         mConnectivityServiceHandler = connectivityServiceInternalHandler;
-        mPacProxyInstaller = new PacProxyInstaller(
-                context, connectivityServiceInternalHandler, pacChangedEvent);
+        mPacProxyManager = context.getSystemService(PacProxyManager.class);
+
+        PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
+        mPacProxyManager.addPacProxyInstalledListener(
+                new HandlerExecutor(mConnectivityServiceHandler), listener);
     }
 
     // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -182,7 +201,7 @@
 
             if (!TextUtils.isEmpty(pacFileUrl)) {
                 mConnectivityServiceHandler.post(
-                        () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
+                        () -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
             }
         }
     }
@@ -226,9 +245,9 @@
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ?
                 defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
+        mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
 
-        if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
-                == PacProxyInstaller.DONT_SEND_BROADCAST) {
+        if (!shouldSendBroadcast(proxyInfo)) {
             return;
         }
         if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,6 +263,10 @@
         }
     }
 
+    private boolean shouldSendBroadcast(ProxyInfo proxy) {
+        return Uri.EMPTY.equals(proxy.getPacFileUrl()) || proxy.getPort() > 0;
+    }
+
     /**
      * Sets the global proxy in memory. Also writes the values to the global settings of the device.
      *
@@ -308,10 +331,10 @@
                 return;
             }
 
-            // This call could be coming from the PacProxyInstaller, containing the port of the
+            // This call could be coming from the PacProxyService, containing the port of the
             // local proxy. If this new proxy matches the global proxy then copy this proxy to the
             // global (to get the correct local port), and send a broadcast.
-            // TODO: Switch PacProxyInstaller to have its own message to send back rather than
+            // TODO: Switch PacProxyService to have its own message to send back rather than
             // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
             if ((mGlobalProxy != null) && (proxyInfo != null)
                     && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 027b9af..0e71496 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -791,12 +791,13 @@
     public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
         enforceCrossUserPermission(userId,
                 "no permission to read sync settings for user " + userId);
+        final int callingUid = Binder.getCallingUid();
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
         final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
-            return syncManager.getSyncAdapterTypes(userId);
+            return syncManager.getSyncAdapterTypes(callingUid, userId);
         } finally {
             restoreCallingIdentity(identityToken);
         }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index df87012..ac7e01e 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -89,6 +89,7 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.text.format.TimeMigrationUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -1257,16 +1258,19 @@
                 syncExemptionFlag, callingUid, callingPid, callingPackage);
     }
 
-    public SyncAdapterType[] getSyncAdapterTypes(int userId) {
+    public SyncAdapterType[] getSyncAdapterTypes(int callingUid, int userId) {
         final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
         serviceInfos = mSyncAdapters.getAllServices(userId);
-        SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
-        int i = 0;
+        final List<SyncAdapterType> types = new ArrayList<>(serviceInfos.size());
         for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
-            types[i] = serviceInfo.type;
-            ++i;
+            final String packageName = serviceInfo.type.getPackageName();
+            if (!TextUtils.isEmpty(packageName) && mPackageManagerInternal.filterAppAccess(
+                    packageName, callingUid, userId)) {
+                continue;
+            }
+            types.add(serviceInfo.type);
         }
-        return types;
+        return types.toArray(new SyncAdapterType[] {});
     }
 
     public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index a2b9b96..91b96dc 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -46,7 +46,6 @@
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
-import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
 
 import java.io.PrintWriter;
 
@@ -220,12 +219,14 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
+            HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
+            HighBrightnessModeController hbmController) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
-                ambientBrightnessThresholds, screenBrightnessThresholds, display, context
+                ambientBrightnessThresholds, screenBrightnessThresholds, display, context,
+                hbmController
         );
     }
 
@@ -236,7 +237,8 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
+            HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context,
+            HighBrightnessModeController hbmController) {
         mInjector = injector;
         mContext = context;
         mCallbacks = callbacks;
@@ -273,20 +275,7 @@
         mPendingForegroundAppPackageName = null;
         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-
-        final DisplayDeviceConfig ddConfig =
-                display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
-        HighBrightnessModeData hbmData =
-                ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
-
-        final Runnable hbmChangeCallback = () -> {
-            updateAutoBrightness(true /*sendUpdate*/, false /*userInitiatedChange*/);
-            // TODO: b/175937645 - Callback to DisplayManagerService to indicate a change to the HBM
-            // allowance has been made so that the brightness limits can be calculated
-            // appropriately.
-        };
-        mHbmController = new HighBrightnessModeController(mHandler, brightnessMin, brightnessMax,
-                hbmData, hbmChangeCallback);
+        mHbmController = hbmController;
     }
 
     /**
@@ -327,6 +316,7 @@
     public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
             float brightness, boolean userChangedBrightness, float adjustment,
             boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
+        mHbmController.setAutoBrightnessEnabled(enable);
         // While dozing, the application processor may be suspended which will prevent us from
         // receiving new information from the light sensor. On some devices, we may be able to
         // switch to a wake-up light sensor instead but for now we will simply disable the sensor
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 174d4b2..96a7416 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1128,7 +1128,7 @@
             recordTopInsetLocked(display);
         }
         addDisplayPowerControllerLocked(display);
-        mDisplayStates.append(displayId, Display.STATE_OFF);
+        mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
         mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault);
 
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1204,16 +1204,15 @@
         DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
             final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
-            final int state;
             final int displayId = display.getDisplayIdLocked();
+            final int state = mDisplayStates.get(displayId);
 
-            if (display.isEnabled()) {
-                state = mDisplayStates.get(displayId);
-            } else {
-                state = Display.STATE_OFF;
+            // Only send a request for display state if the display state has already been
+            // initialized by DisplayPowercontroller.
+            if (state != Display.STATE_UNKNOWN) {
+                final float brightness = mDisplayBrightnesses.get(displayId);
+                return device.requestDisplayStateLocked(state, brightness);
             }
-            final float brightness = mDisplayBrightnesses.get(displayId);
-            return device.requestDisplayStateLocked(state, brightness);
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 0117326..e44ecac 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -350,12 +350,15 @@
     private final ColorDisplayServiceInternal mCdsi;
     private final float[] mNitsRange;
 
+    private final HighBrightnessModeController mHbmController;
+
     // A record of state for skipping brightness ramps.
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
     // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
     private float mInitialAutoBrightness;
 
+
     // The controller for the automatic brightness level.
     private AutomaticBrightnessController mAutomaticBrightnessController;
 
@@ -433,6 +436,7 @@
         mBlanker = blanker;
         mContext = context;
 
+
         PowerManager pm = context.getSystemService(PowerManager.class);
 
         final Resources resources = context.getResources();
@@ -476,6 +480,8 @@
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
+        mHbmController = createHbmController();
+
         if (mUseSoftwareAutoBrightnessConfig) {
             final float dozeScaleFactor = resources.getFraction(
                     com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
@@ -535,7 +541,7 @@
                         PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds, logicalDisplay, context);
+                        screenBrightnessThresholds, logicalDisplay, context, mHbmController);
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
@@ -982,7 +988,8 @@
             mWaitingForNegativeProximity = false;
             mIgnoreProximityUntilChanged = false;
         }
-        if (mScreenOffBecauseOfProximity) {
+
+        if (!mLogicalDisplay.isEnabled() || mScreenOffBecauseOfProximity) {
             state = Display.STATE_OFF;
         }
 
@@ -1080,6 +1087,8 @@
             mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
         }
 
+        boolean updateScreenBrightnessSetting = false;
+
         // Apply auto-brightness.
         boolean slowChange = false;
         if (Float.isNaN(brightnessState)) {
@@ -1096,11 +1105,7 @@
                 if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                     slowChange = true; // slowly adapt to auto-brightness
                 }
-                // Tell the rest of the system about the new brightness. Note that we do this
-                // before applying the low power or dim transformations so that the slider
-                // accurately represents the full possible range, even if they range changes what
-                // it means in absolute terms.
-                putScreenBrightnessSetting(brightnessState);
+                updateScreenBrightnessSetting = true;
                 mAppliedAutoBrightness = true;
                 mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
             } else {
@@ -1118,6 +1123,7 @@
             mAppliedAutoBrightness = false;
             brightnessAdjustmentFlags = 0;
         }
+
         // Use default brightness when dozing unless overridden.
         if ((Float.isNaN(brightnessState))
                 && Display.isDozeState(state)) {
@@ -1128,9 +1134,24 @@
         // Apply manual brightness.
         if (Float.isNaN(brightnessState)) {
             brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+            if (brightnessState != mCurrentScreenBrightnessSetting) {
+                // The manually chosen screen brightness is outside of the currently allowed
+                // range (i.e., high-brightness-mode), make sure we tell the rest of the system
+                // by updating the setting.
+                updateScreenBrightnessSetting = true;
+            }
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
         }
 
+        if (updateScreenBrightnessSetting) {
+            // Tell the rest of the system about the new brightness in case we had to change it
+            // for things like auto-brightness or high-brightness-mode. Note that we do this
+            // before applying the low power or dim transformations so that the slider
+            // accurately represents the full possible range, even if they range changes what
+            // it means in absolute terms.
+            putScreenBrightnessSetting(brightnessState);
+        }
+
         // Apply dimming by at least some minimum amount when user activity
         // timeout is about to expire.
         if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
@@ -1208,9 +1229,10 @@
             // animate to. To avoid this, we check the value first.
             // If the brightnessState is off (-1.0f) we still want to animate to the minimum
             // brightness (0.0f) to accommodate for LED displays, which can appear bright to the
-            // user even when the display is all black.
-            float animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT
-                    ? PowerManager.BRIGHTNESS_MIN : brightnessState;
+            // user even when the display is all black. We also clamp here in case some
+            // transformations to the brightness have pushed it outside of the currently
+            // allowed range.
+            float animateValue = clampScreenBrightness(brightnessState);
             final float currentBrightness = mPowerState.getScreenBrightness();
             if (isValidBrightnessValue(animateValue)
                     && !BrightnessSynchronizer.floatEquals(animateValue, currentBrightness)) {
@@ -1353,6 +1375,15 @@
         msg.sendToTarget();
     }
 
+    private HighBrightnessModeController createHbmController() {
+        final DisplayDeviceConfig ddConfig =
+                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+        final DisplayDeviceConfig.HighBrightnessModeData hbmData =
+                ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
+        return new HighBrightnessModeController(mHandler, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX, hbmData, () -> sendUpdatePowerStateLocked());
+    }
+
     private void blockScreenOn() {
         if (mPendingScreenOnUnblocker == null) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -1468,10 +1499,10 @@
 
     private float clampScreenBrightness(float value) {
         if (Float.isNaN(value)) {
-            return PowerManager.BRIGHTNESS_MIN;
+            value = PowerManager.BRIGHTNESS_MIN;
         }
-        return MathUtils.constrain(
-                value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+        return MathUtils.constrain(value,
+                mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
     }
 
     // Checks whether the brightness is within the valid brightness range, not including the off or
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 12b810f..2e5561d 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,7 +37,7 @@
 class HighBrightnessModeController {
     private static final String TAG = "HighBrightnessModeController";
 
-    private static final boolean DEBUG_HBM = false;
+    private static final boolean DEBUG = false;
 
     private final float mBrightnessMin;
     private final float mBrightnessMax;
@@ -48,6 +48,7 @@
 
     private boolean mIsInAllowedAmbientRange = false;
     private boolean mIsTimeAvailable = false;
+    private boolean mIsAutoBrightnessEnabled = false;
     private float mAutoBrightness;
 
     /**
@@ -84,6 +85,17 @@
         };
     }
 
+    void setAutoBrightnessEnabled(boolean isEnabled) {
+        if (isEnabled == mIsAutoBrightnessEnabled) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "setAutoBrightness( " + isEnabled + " )");
+        }
+        mIsAutoBrightnessEnabled = isEnabled;
+        mIsInAllowedAmbientRange = false; // reset when auto-brightness switches
+    }
+
     float getCurrentBrightnessMin() {
         return mBrightnessMin;
     }
@@ -102,7 +114,7 @@
     }
 
     void onAmbientLuxChange(float ambientLux) {
-        if (!deviceSupportsHbm()) {
+        if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) {
             return;
         }
 
@@ -132,7 +144,7 @@
                 mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
                 mRunningStartTimeMillis = -1;
 
-                if (DEBUG_HBM) {
+                if (DEBUG) {
                     Slog.d(TAG, "New HBM event: " + mEvents.getFirst());
                 }
             }
@@ -142,7 +154,7 @@
     }
 
     private boolean isCurrentlyAllowed() {
-        return mIsTimeAvailable && mIsInAllowedAmbientRange;
+        return mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange;
     }
 
     private boolean deviceSupportsHbm() {
@@ -167,7 +179,7 @@
             timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
         }
 
-        if (DEBUG_HBM) {
+        if (DEBUG) {
             Slog.d(TAG, "Time already used after current session: " + timeAlreadyUsed);
         }
 
@@ -187,7 +199,7 @@
             timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
         }
 
-        if (DEBUG_HBM) {
+        if (DEBUG) {
             Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed);
         }
 
@@ -220,7 +232,7 @@
             nextTimeout = timeWhenMinIsGainedBack;
         }
 
-        if (DEBUG_HBM) {
+        if (DEBUG) {
             Slog.d(TAG, "HBM recalculated.  IsAllowedWithoutRestrictions: "
                     + isAllowedWithoutRestrictions
                     + ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d88896c..aaec89a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -682,7 +682,10 @@
                                     || oldState == Display.STATE_ON_SUSPEND) {
                                 setDisplayState(Display.STATE_ON);
                                 currentState = Display.STATE_ON;
-                            } else {
+
+                            // If UNKNOWN, we still want to set the initial display state,
+                            // otherwise, return early.
+                            } else if (oldState != Display.STATE_UNKNOWN) {
                                 return; // old state and new state is off
                             }
                         }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 06adce8..2b7d207 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -31,6 +31,7 @@
 import android.os.ShellCallback;
 import android.system.ErrnoException;
 import android.text.FontConfig;
+import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -66,6 +67,8 @@
 
     @Override
     public FontConfig getFontConfig() {
+        getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
+                "UPDATE_FONTS permission required.");
         return getSystemFontConfig();
     }
 
@@ -148,10 +151,24 @@
 
     /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
         @Override
-        public String getPostScriptName(File file) throws IOException {
+        public String getCanonicalFileName(File file) throws IOException {
             ByteBuffer buffer = mmap(file);
             try {
-                return FontFileUtil.getPostScriptName(buffer, 0);
+                String psName = FontFileUtil.getPostScriptName(buffer, 0);
+                int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
+                int isCollection = FontFileUtil.isCollectionFont(buffer);
+
+                if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
+                    return null;
+                }
+
+                String extension;
+                if (isCollection == 1) {
+                    extension = isType1Font == 1 ? ".otc" : ".ttc";
+                } else {
+                    extension = isType1Font == 1 ? ".otf" : ".ttf";
+                }
+                return psName + extension;
             } finally {
                 NioUtils.freeDirectBuffer(buffer);
             }
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 86dbe86..4f95d27 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -56,14 +56,12 @@
 
     private static final String TAG = "UpdatableFontDir";
     private static final String RANDOM_DIR_PREFIX = "~~";
-    // TODO: Support .otf
-    private static final String ALLOWED_EXTENSION = ".ttf";
 
     private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
 
     /** Interface to mock font file access in tests. */
     interface FontFileParser {
-        String getPostScriptName(File file) throws IOException;
+        String getCanonicalFileName(File file) throws IOException;
 
         long getRevision(File file) throws IOException;
     }
@@ -321,20 +319,20 @@
                         FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
                         "Failed to setup fs-verity.", e);
             }
-            String postScriptName;
+            String canonicalFileName;
             try {
-                postScriptName = mParser.getPostScriptName(tempNewFontFile);
+                canonicalFileName = mParser.getCanonicalFileName(tempNewFontFile);
             } catch (IOException e) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_INVALID_FONT_FILE,
                         "Failed to read PostScript name from font file", e);
             }
-            if (postScriptName == null) {
+            if (canonicalFileName == null) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_INVALID_FONT_NAME,
                         "Failed to read PostScript name from font file");
             }
-            File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+            File newFontFile = new File(newDir, canonicalFileName);
             if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
@@ -380,20 +378,38 @@
         return dir;
     }
 
+    private FontFileInfo lookupFontFileInfo(File file) {
+        String name = file.getName();
+
+        if (!name.endsWith(".ttf") && !name.endsWith(".otf") && !name.endsWith(".ttc")
+                && !name.endsWith(".otc")) {
+            return null;
+        }
+        String key = name.substring(0, name.length() - 4);
+        return mFontFileInfoMap.get(key);
+    }
+
+    private void putFontFileInfo(FontFileInfo info) {
+        String name = info.getFile().getName();
+        // The file name in FontFileInfo is already validated. Thus, just strip last 4 chars.
+        String key = name.substring(0, name.length() - 4);
+        mFontFileInfoMap.put(key, info);
+    }
+
     /**
      * Add the given {@link FontFileInfo} to {@link #mFontFileInfoMap} if its font revision is
      * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
      * #mPreinstalledFontDirs}).
      */
     private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
-        String name = fontFileInfo.getFile().getName();
-        FontFileInfo existingInfo = mFontFileInfoMap.get(name);
+        FontFileInfo existingInfo = lookupFontFileInfo(fontFileInfo.getFile());
         final boolean shouldAddToMap;
         if (existingInfo == null) {
             // We got a new updatable font. We need to check if it's newer than preinstalled fonts.
             // Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font
             // with 'name'.
-            shouldAddToMap = getPreinstalledFontRevision(name) < fontFileInfo.getRevision();
+            long preInstalledRev = getPreinstalledFontRevision(fontFileInfo.getFile().getName());
+            shouldAddToMap = preInstalledRev < fontFileInfo.getRevision();
         } else {
             shouldAddToMap = existingInfo.getRevision() < fontFileInfo.getRevision();
         }
@@ -401,7 +417,7 @@
             if (deleteOldFile && existingInfo != null) {
                 FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir());
             }
-            mFontFileInfoMap.put(name, fontFileInfo);
+            putFontFileInfo(fontFileInfo);
         } else {
             if (deleteOldFile) {
                 FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir());
@@ -464,15 +480,18 @@
      */
     private boolean validateFontFileName(File file) {
         String fileName = file.getName();
-        String postScriptName = getPostScriptName(file);
-        return (postScriptName + ALLOWED_EXTENSION).equals(fileName);
+        String canonicalFileName = getCanonicalFileName(file);
+        if (canonicalFileName == null) {
+            return false;
+        }
+        return canonicalFileName.equals(fileName);
     }
 
     /** Returns the PostScript name of the given font file, or null. */
     @Nullable
-    private String getPostScriptName(File file) {
+    private String getCanonicalFileName(File file) {
         try {
-            return mParser.getPostScriptName(file);
+            return mParser.getCanonicalFileName(file);
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read font file", e);
             return null;
@@ -514,7 +533,7 @@
         List<FontConfig.Font> fontList = fontFamily.getFontList();
         for (int i = 0; i < fontList.size(); i++) {
             FontConfig.Font font = fontList.get(i);
-            FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName());
+            FontFileInfo info = lookupFontFileInfo(font.getFile());
             if (info == null) {
                 return null;
             }
@@ -537,8 +556,9 @@
 
     Map<String, File> getFontFileMap() {
         Map<String, File> map = new ArrayMap<>();
-        for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
-            map.put(entry.getKey(), entry.getValue().getFile());
+        for (int i = 0; i < mFontFileInfoMap.size(); ++i) {
+            File file = mFontFileInfoMap.valueAt(i).getFile();
+            map.put(file.getName(), file);
         }
         return map;
     }
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index c23e2e6..f6828d1 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -97,16 +97,25 @@
 
     @Override
     public boolean start() {
-      // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
-      // The message is re-sent at the end of the action for devices that don't support 2.0.
-      sendSetStreamPath();
-        int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
-                .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus();
-        if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+        // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
+        // The message is re-sent at the end of the action for devices that don't support 2.0.
+        sendSetStreamPath();
+
+        if (!mIsCec20) {
             queryDevicePowerStatus();
-        } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
-            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
-            return true;
+        } else {
+            int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+            HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork()
+                    .getCecDeviceInfo(getTargetAddress());
+            if (targetDevice != null) {
+                targetPowerStatus = targetDevice.getDevicePowerStatus();
+            }
+            if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+                queryDevicePowerStatus();
+            } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+                finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+                return true;
+            }
         }
         mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 624af30..6fbb26c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -35,33 +35,19 @@
 import android.os.UserHandle;
 import android.provider.Settings.Global;
 import android.util.ArrayMap;
-import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ConcurrentUtils;
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.Setting;
-import com.android.server.hdmi.cec.config.Value;
-import com.android.server.hdmi.cec.config.XmlParser;
 
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
-import javax.xml.datatype.DatatypeConfigurationException;
-
 /**
  * The {@link HdmiCecConfig} class is used for getting information about
  * available HDMI CEC settings.
@@ -74,6 +60,10 @@
     private static final String SHARED_PREFS_DIR = "shared_prefs";
     private static final String SHARED_PREFS_NAME = "cec_config.xml";
 
+    private static final int STORAGE_SYSPROPS = 0;
+    private static final int STORAGE_GLOBAL_SETTINGS = 1;
+    private static final int STORAGE_SHARED_PREFS = 2;
+
     @IntDef({
         STORAGE_SYSPROPS,
         STORAGE_GLOBAL_SETTINGS,
@@ -81,10 +71,6 @@
     })
     private @interface Storage {}
 
-    private static final int STORAGE_SYSPROPS = 0;
-    private static final int STORAGE_GLOBAL_SETTINGS = 1;
-    private static final int STORAGE_SHARED_PREFS = 2;
-
     private static final String VALUE_TYPE_STRING = "string";
     private static final String VALUE_TYPE_INT = "int";
 
@@ -96,8 +82,6 @@
 
     @NonNull private final Context mContext;
     @NonNull private final StorageAdapter mStorageAdapter;
-    @Nullable private final CecSettings mSystemConfig;
-    @Nullable private final CecSettings mVendorOverride;
 
     private final Object mLock = new Object();
 
@@ -107,6 +91,18 @@
 
     private SettingsObserver mSettingsObserver;
 
+    private LinkedHashMap<String, Setting> mSettings = new LinkedHashMap<>();
+
+    /**
+     * Exception thrown when the CEC Configuration setup verification fails.
+     * This usually means a settings lacks default value or storage/storage key.
+     */
+    public static class VerificationException extends RuntimeException {
+        public VerificationException(String message) {
+            super(message);
+        }
+    }
+
     /**
      * Listener used to get notifications when value of a setting changes.
      */
@@ -202,91 +198,297 @@
         }
     }
 
+    private class Value {
+        private final String mStringValue;
+        private final Integer mIntValue;
+
+        Value(@NonNull String value) {
+            mStringValue = value;
+            mIntValue = null;
+        }
+
+        Value(@NonNull Integer value) {
+            mStringValue = null;
+            mIntValue = value;
+        }
+
+        String getStringValue() {
+            return mStringValue;
+        }
+
+        Integer getIntValue() {
+            return mIntValue;
+        }
+    }
+
+    private class Setting {
+        @NonNull private final Context mContext;
+        @NonNull private final @CecSettingName String mName;
+        private final boolean mUserConfigurable;
+
+        private Value mDefaultValue = null;
+        private List<Value> mAllowedValues = new ArrayList<>();
+
+        Setting(@NonNull Context context,
+                @NonNull @CecSettingName String name,
+                int userConfResId) {
+            mContext = context;
+            mName = name;
+            mUserConfigurable = mContext.getResources().getBoolean(userConfResId);
+        }
+
+        public @CecSettingName String getName() {
+            return mName;
+        }
+
+        public @ValueType String getValueType() {
+            return getDefaultValue().getStringValue() != null
+                    ? VALUE_TYPE_STRING
+                    : VALUE_TYPE_INT;
+        }
+
+        public Value getDefaultValue() {
+            if (mDefaultValue == null) {
+                throw new VerificationException("Invalid CEC setup for '"
+                    + this.getName() + "' setting. "
+                    + "Setting has no default value.");
+            }
+            return mDefaultValue;
+        }
+
+        public boolean getUserConfigurable() {
+            return mUserConfigurable;
+        }
+
+        private void registerValue(@NonNull Value value,
+                                   int allowedResId, int defaultResId) {
+            if (mContext.getResources().getBoolean(allowedResId)) {
+                mAllowedValues.add(value);
+                if (mContext.getResources().getBoolean(defaultResId)) {
+                    if (mDefaultValue != null) {
+                        throw new VerificationException("Invalid CEC setup for '"
+                            + this.getName() + "' setting. "
+                            + "Setting already has a default value.");
+                    }
+                    mDefaultValue = value;
+                }
+            }
+        }
+
+        public void registerValue(@NonNull String value, int allowedResId,
+                                  int defaultResId) {
+            registerValue(new Value(value), allowedResId, defaultResId);
+        }
+
+        public void registerValue(int value, int allowedResId,
+                                  int defaultResId) {
+            registerValue(new Value(value), allowedResId, defaultResId);
+        }
+
+
+        public List<Value> getAllowedValues() {
+            return mAllowedValues;
+        }
+    }
+
     @VisibleForTesting
     HdmiCecConfig(@NonNull Context context,
-                  @NonNull StorageAdapter storageAdapter,
-                  @Nullable CecSettings systemConfig,
-                  @Nullable CecSettings vendorOverride) {
+                  @NonNull StorageAdapter storageAdapter) {
         mContext = context;
         mStorageAdapter = storageAdapter;
-        mSystemConfig = systemConfig;
-        mVendorOverride = vendorOverride;
-        if (mSystemConfig == null) {
-            Slog.i(TAG, "CEC system configuration XML missing.");
-        }
-        if (mVendorOverride == null) {
-            Slog.i(TAG, "CEC OEM configuration override XML missing.");
-        }
+
+        Setting hdmiCecEnabled = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+                R.bool.config_cecHdmiCecEnabled_userConfigurable);
+        hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED,
+                R.bool.config_cecHdmiCecControlEnabled_allowed,
+                R.bool.config_cecHdmiCecControlEnabled_default);
+        hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
+                R.bool.config_cecHdmiCecControlDisabled_allowed,
+                R.bool.config_cecHdmiCecControlDisabled_default);
+
+        Setting hdmiCecVersion = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                R.bool.config_cecHdmiCecVersion_userConfigurable);
+        hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                R.bool.config_cecHdmiCecVersion14b_allowed,
+                R.bool.config_cecHdmiCecVersion14b_default);
+        hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                R.bool.config_cecHdmiCecVersion20_allowed,
+                R.bool.config_cecHdmiCecVersion20_default);
+
+        Setting powerControlMode = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                R.bool.config_cecSendStandbyOnSleep_userConfigurable);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
+                R.bool.config_cecPowerControlModeTv_allowed,
+                R.bool.config_cecPowerControlModeTv_default);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
+                R.bool.config_cecPowerControlModeBroadcast_allowed,
+                R.bool.config_cecPowerControlModeBroadcast_default);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE,
+                R.bool.config_cecPowerControlModeNone_allowed,
+                R.bool.config_cecPowerControlModeNone_default);
+
+        Setting powerStateChangeOnActiveSourceLost = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
+        powerStateChangeOnActiveSourceLost.registerValue(
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
+        powerStateChangeOnActiveSourceLost.registerValue(
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+
+        Setting systemAudioModeMuting = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                R.bool.config_cecSystemAudioModeMuting_userConfigurable);
+        systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+                R.bool.config_cecSystemAudioModeMutingEnabled_allowed,
+                R.bool.config_cecSystemAudioModeMutingEnabled_default);
+        systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED,
+                R.bool.config_cecSystemAudioModeMutingDisabled_allowed,
+                R.bool.config_cecSystemAudioModeMutingDisabled_default);
+
+        Setting volumeControlMode = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                R.bool.config_cecVolumeControlMode_userConfigurable);
+        volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_ENABLED,
+                R.bool.config_cecVolumeControlModeEnabled_allowed,
+                R.bool.config_cecVolumeControlModeEnabled_default);
+        volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_DISABLED,
+                R.bool.config_cecVolumeControlModeDisabled_allowed,
+                R.bool.config_cecVolumeControlModeDisabled_default);
+
+        Setting tvWakeOnOneTouchPlay = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
+        tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED,
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed,
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
+        tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED,
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed,
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
+
+        Setting tvSendStandbyOnSleep = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
+        tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED,
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed,
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
+        tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_DISABLED,
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed,
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+
+        Setting rcProfileTv = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                R.bool.config_cecRcProfileTv_userConfigurable);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_NONE,
+                R.bool.config_cecRcProfileTvNone_allowed,
+                R.bool.config_cecRcProfileTvNone_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_ONE,
+                R.bool.config_cecRcProfileTvOne_allowed,
+                R.bool.config_cecRcProfileTvOne_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_TWO,
+                R.bool.config_cecRcProfileTvTwo_allowed,
+                R.bool.config_cecRcProfileTvTwo_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_THREE,
+                R.bool.config_cecRcProfileTvThree_allowed,
+                R.bool.config_cecRcProfileTvThree_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_FOUR,
+                R.bool.config_cecRcProfileTvFour_allowed,
+                R.bool.config_cecRcProfileTvFour_default);
+
+        Setting rcProfileSourceRootMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
+        rcProfileSourceRootMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceRootMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceRootMenuHandled_default);
+        rcProfileSourceRootMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
+
+        Setting rcProfileSourceSetupMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
+        rcProfileSourceSetupMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
+        rcProfileSourceSetupMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
+
+        Setting rcProfileSourceContentsMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
+        rcProfileSourceContentsMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
+        rcProfileSourceContentsMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
+
+        Setting rcProfileSourceTopMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
+        rcProfileSourceTopMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceTopMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceTopMenuHandled_default);
+        rcProfileSourceTopMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
+
+        Setting rcProfileSourceMediaContextSensitiveMenu = registerSetting(
+                HdmiControlManager
+                    .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
+        rcProfileSourceMediaContextSensitiveMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
+        rcProfileSourceMediaContextSensitiveMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
+
+        verifySettings();
     }
 
     HdmiCecConfig(@NonNull Context context) {
-        this(context, new StorageAdapter(context),
-             readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(),
-                                                        ETC_DIR, CONFIG_FILE)),
-             readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
-                                                        ETC_DIR, CONFIG_FILE)));
+        this(context, new StorageAdapter(context));
     }
 
-    @Nullable
-    private static CecSettings readSettingsFromFile(@NonNull File file) {
-        if (!file.exists()) {
-            return null;
-        }
-        if (!file.isFile()) {
-            Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
-            return null;
-        }
-        try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
-            return XmlParser.read(in);
-        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
-            Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
-        }
-        return null;
+    private Setting registerSetting(@NonNull @CecSettingName String name,
+                               int userConfResId) {
+        Setting setting = new Setting(mContext, name, userConfResId);
+        mSettings.put(name, setting);
+        return setting;
     }
 
-    @NonNull
-    @VisibleForTesting
-    static HdmiCecConfig createFromStrings(@NonNull Context context,
-                                           @NonNull StorageAdapter storageAdapter,
-                                           @Nullable String productConfigXml,
-                                           @Nullable String vendorOverrideXml) {
-        CecSettings productConfig = null;
-        CecSettings vendorOverride = null;
-        try {
-            if (productConfigXml != null) {
-                productConfig = XmlParser.read(
-                        new ByteArrayInputStream(productConfigXml.getBytes()));
-            }
-            if (vendorOverrideXml != null) {
-                vendorOverride = XmlParser.read(
-                        new ByteArrayInputStream(vendorOverrideXml.getBytes()));
-            }
-        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
-            Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+    private void verifySettings() {
+        for (Setting setting: mSettings.values()) {
+            // This will throw an exception when a setting
+            // doesn't have a default value assigned.
+            setting.getDefaultValue();
+            getStorage(setting);
+            getStorageKey(setting);
         }
-        return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride);
     }
 
     @Nullable
     private Setting getSetting(@NonNull String name) {
-        if (mSystemConfig == null) {
-            return null;
-        }
-        if (mVendorOverride != null) {
-            // First read from the vendor override.
-            for (Setting setting : mVendorOverride.getSetting()) {
-                if (setting.getName().equals(name)) {
-                    return setting;
-                }
-            }
-        }
-        // If not found, try the system config.
-        for (Setting setting : mSystemConfig.getSetting()) {
-            if (setting.getName().equals(name)) {
-                return setting;
-            }
-        }
-        return null;
+        return mSettings.containsKey(name) ? mSettings.get(name) : null;
     }
 
     @Storage
@@ -322,7 +524,7 @@
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
                 return STORAGE_SHARED_PREFS;
             default:
-                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                throw new VerificationException("Invalid CEC setting '" + setting.getName()
                         + "' storage.");
         }
     }
@@ -359,7 +561,7 @@
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
                 return setting.getName();
             default:
-                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                throw new VerificationException("Invalid CEC setting '" + setting.getName()
                     + "' storage key.");
         }
     }
@@ -396,10 +598,6 @@
         }
     }
 
-    private int getIntValue(@NonNull Value value) {
-        return Integer.decode(value.getIntValue());
-    }
-
     private void notifyGlobalSettingChanged(String setting) {
         switch (setting) {
             case Global.HDMI_CONTROL_ENABLED:
@@ -533,41 +731,20 @@
      * Returns a list of all settings based on the XML metadata.
      */
     public @CecSettingName List<String> getAllSettings() {
-        if (mSystemConfig == null) {
-            return new ArrayList<String>();
-        }
-        List<String> allSettings = new ArrayList<String>();
-        for (Setting setting : mSystemConfig.getSetting()) {
-            allSettings.add(setting.getName());
-        }
-        return allSettings;
+        return new ArrayList<>(mSettings.keySet());
     }
 
     /**
      * Returns a list of user-modifiable settings based on the XML metadata.
      */
     public @CecSettingName List<String> getUserSettings() {
-        if (mSystemConfig == null) {
-            return new ArrayList<String>();
-        }
-        Set<String> userSettings = new HashSet<String>();
-        // First read from the system config.
-        for (Setting setting : mSystemConfig.getSetting()) {
+        List<String> settings = new ArrayList<>();
+        for (Setting setting: mSettings.values()) {
             if (setting.getUserConfigurable()) {
-                userSettings.add(setting.getName());
+                settings.add(setting.getName());
             }
         }
-        if (mVendorOverride != null) {
-            // Next either add or remove based on the vendor override.
-            for (Setting setting : mVendorOverride.getSetting()) {
-                if (setting.getUserConfigurable()) {
-                    userSettings.add(setting.getName());
-                } else {
-                    userSettings.remove(setting.getName());
-                }
-            }
-        }
-        return new ArrayList(userSettings);
+        return settings;
     }
 
     /**
@@ -607,7 +784,7 @@
                     + "' is not a string-type setting.");
         }
         List<String> allowedValues = new ArrayList<String>();
-        for (Value allowedValue : setting.getAllowedValues().getValue()) {
+        for (Value allowedValue : setting.getAllowedValues()) {
             allowedValues.add(allowedValue.getStringValue());
         }
         return allowedValues;
@@ -626,8 +803,8 @@
                     + "' is not a string-type setting.");
         }
         List<Integer> allowedValues = new ArrayList<Integer>();
-        for (Value allowedValue : setting.getAllowedValues().getValue()) {
-            allowedValues.add(getIntValue(allowedValue));
+        for (Value allowedValue : setting.getAllowedValues()) {
+            allowedValues.add(allowedValue.getIntValue());
         }
         return allowedValues;
     }
@@ -659,7 +836,7 @@
             throw new IllegalArgumentException("Setting '" + name
                     + "' is not a string-type setting.");
         }
-        return getIntValue(getSetting(name).getDefaultValue());
+        return getSetting(name).getDefaultValue().getIntValue();
     }
 
     /**
@@ -691,7 +868,7 @@
                     + "' is not a int-type setting.");
         }
         HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
-        String defaultValue = Integer.toString(getIntValue(setting.getDefaultValue()));
+        String defaultValue = Integer.toString(setting.getDefaultValue().getIntValue());
         String value = retrieveValue(setting, defaultValue);
         return Integer.parseInt(value);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index d8914b3..bdc4e66 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -500,7 +500,8 @@
 
         HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
         // If no non-default display name is available for the device, request the devices OSD name.
-        if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) {
+        if (cecDeviceInfo != null && cecDeviceInfo.getDisplayName().equals(
+                HdmiUtils.getDefaultDeviceName(address))) {
             mService.sendCecCommand(
                     HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7235a92..90d6433 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -30,6 +30,7 @@
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL;
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
 
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -1147,6 +1148,7 @@
                 && getAvrDeviceInfo() != null;
     }
 
+    @Nullable
     @ServiceThreadOnly
     HdmiDeviceInfo getAvrDeviceInfo() {
         assertRunOnServiceThread();
@@ -1157,6 +1159,7 @@
         return getSafeAvrDeviceInfo() != null;
     }
 
+    @Nullable
     HdmiDeviceInfo getSafeAvrDeviceInfo() {
         return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index b748ae0..7ceaa95 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -185,6 +185,12 @@
         mLocalDevices.clear();
     }
 
+    /**
+     * Get the device info of a local device or a device in the CEC network by a device id.
+     * @param id id of the device to get
+     * @return the device with the given id, or {@code null}
+     */
+    @Nullable
     public HdmiDeviceInfo getDeviceInfo(int id) {
         return mDeviceInfos.get(id);
     }
@@ -717,6 +723,7 @@
      * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
      * Returns null if no logical address matched
      */
+    @Nullable
     HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
         for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
             if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 115cafed..e6e2f96 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -988,6 +988,7 @@
         return mCecController.getVendorId();
     }
 
+    @Nullable
     @ServiceThreadOnly
     HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 73ecbe0..979e7a4 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -16,6 +16,7 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.util.Slog;
@@ -62,8 +63,7 @@
             Slog.e(TAG, "Wrong arguments");
             return null;
         }
-        return new OneTouchPlayAction(source, targetAddress,
-                callback);
+        return new OneTouchPlayAction(source, targetAddress, callback);
     }
 
     private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
@@ -71,8 +71,8 @@
         this(localDevice, targetAddress, callback,
                 localDevice.getDeviceInfo().getCecVersion()
                         >= HdmiControlManager.HDMI_CEC_VERSION_2_0
-                        && localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
-                        targetAddress).getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
+                        && getTargetCecVersion(localDevice, targetAddress)
+                        >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
     }
 
     @VisibleForTesting
@@ -88,9 +88,9 @@
         // Because only source device can create this action, it's safe to cast.
         mSource = source();
         sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
-        boolean targetOnBefore = localDevice().mService.getHdmiCecNetwork()
-                .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus()
-                == HdmiControlManager.POWER_STATUS_ON;
+
+        boolean is20TargetOnBefore = mIsCec20 && getTargetDevicePowerStatus(mSource, mTargetAddress,
+                HdmiControlManager.POWER_STATUS_UNKNOWN) == HdmiControlManager.POWER_STATUS_ON;
         broadcastActiveSource();
         // If the device is not an audio system itself, request the connected audio system to
         // turn on.
@@ -98,18 +98,23 @@
             sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(),
                     Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true));
         }
-        int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
-                .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus();
-        if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+
+        if (!mIsCec20) {
             queryDevicePowerStatus();
-        } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
-            if (!targetOnBefore) {
-                // Suppress 2nd <Active Source> message if the target device was already on when
-                // the 1st one was sent.
-                broadcastActiveSource();
+        } else {
+            int targetPowerStatus = getTargetDevicePowerStatus(mSource, mTargetAddress,
+                    HdmiControlManager.POWER_STATUS_UNKNOWN);
+            if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+                queryDevicePowerStatus();
+            } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+                if (!is20TargetOnBefore) {
+                    // Suppress 2nd <Active Source> message if the target device was already on when
+                    // the 1st one was sent.
+                    broadcastActiveSource();
+                }
+                finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+                return true;
             }
-            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
-            return true;
         }
         mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
@@ -179,4 +184,23 @@
         return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
+    private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
+            int targetLogicalAddress) {
+        HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+                targetLogicalAddress);
+        if (targetDevice != null) {
+            return targetDevice.getCecVersion();
+        }
+        return HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+    }
+
+    private static int getTargetDevicePowerStatus(HdmiCecLocalDevice localDevice,
+            int targetLogicalAddress, int defaultPowerStatus) {
+        HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+                targetLogicalAddress);
+        if (targetDevice != null) {
+            return targetDevice.getDevicePowerStatus();
+        }
+        return defaultPowerStatus;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index f7e871d..56e538b 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -17,6 +17,8 @@
 package com.android.server.hdmi;
 
 import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
 
 /**
@@ -30,6 +32,14 @@
     // <Give System Audio Mode Status> to AV Receiver.
     private static final int STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS = 1;
 
+    @VisibleForTesting
+    static final int RETRIES_ON_TIMEOUT = 1;
+
+    // On some audio devices the <System Audio Mode Status> message can be delayed as the device
+    // is just waking up. Retry the <Give System Audio Mode Status> message to ensure we properly
+    // initialize system audio.
+    private int mRetriesOnTimeOut = RETRIES_ON_TIMEOUT;
+
     SystemAudioAutoInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
         super(source);
         mAvrAddress = avrAddress;
@@ -100,6 +110,13 @@
 
         switch (mState) {
             case STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS:
+                if (mRetriesOnTimeOut > 0) {
+                    mRetriesOnTimeOut--;
+                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
+                    sendGiveSystemAudioModeStatus();
+                    return;
+                }
+
                 handleSystemAudioModeStatusTimeout();
                 break;
         }
diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml
deleted file mode 100644
index 191e725..0000000
--- a/services/core/java/com/android/server/hdmi/cec_config.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<cec-settings>
-  <setting name="hdmi_cec_enabled"
-           value-type="int"
-           user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="hdmi_cec_version"
-           value-type="int"
-           user-configurable="true">
-    <allowed-values>
-      <value int-value="0x05" />
-      <value int-value="0x06" />
-    </allowed-values>
-    <default-value int-value="0x05" />
-  </setting>
-  <setting name="send_standby_on_sleep"
-           value-type="string"
-           user-configurable="true">
-    <allowed-values>
-      <value string-value="to_tv" />
-      <value string-value="broadcast" />
-      <value string-value="none" />
-    </allowed-values>
-    <default-value string-value="to_tv" />
-  </setting>
-  <setting name="power_state_change_on_active_source_lost"
-           value-type="string"
-           user-configurable="true">
-    <allowed-values>
-      <value string-value="none" />
-      <value string-value="standby_now" />
-    </allowed-values>
-    <default-value string-value="none" />
-  </setting>
-  <setting name="system_audio_mode_muting"
-           value-type="int"
-           user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="volume_control_enabled"
-      value-type="int"
-      user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="tv_wake_on_one_touch_play"
-      value-type="int"
-      user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="tv_send_standby_on_sleep"
-      value-type="int"
-      user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="rc_profile_tv"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0x0" />
-      <value int-value="0x2" />
-      <value int-value="0x6" />
-      <value int-value="0xA" />
-      <value int-value="0xE" />
-    </allowed-values>
-    <default-value int-value="0x0" />
-  </setting>
-  <setting name="rc_profile_source_handles_root_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="rc_profile_source_handles_setup_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="rc_profile_source_handles_contents_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="0" />
-  </setting>
-  <setting name="rc_profile_source_handles_top_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="0" />
-  </setting>
-  <setting name="rc_profile_source_handles_media_context_sensitive_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="0" />
-  </setting>
-</cec-settings>
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7dc9a0b..cbe6e69 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -304,7 +304,7 @@
             int displayId, InputApplicationHandle application);
     private static native void nativeSetFocusedDisplay(long ptr, int displayId);
     private static native boolean nativeTransferTouchFocus(long ptr,
-            IBinder fromChannelToken, IBinder toChannelToken);
+            IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
     private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1727,12 +1727,14 @@
      * @param fromChannel The channel of a window that currently has touch focus.
      * @param toChannel The channel of the window that should receive touch focus in
      * place of the first.
+     * @param isDragDrop True if transfer touch focus for drag and drop.
      * @return True if the transfer was successful.  False if the window with the
      * specified channel did not actually have touch focus at the time of the request.
      */
     public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
-            @NonNull InputChannel toChannel) {
-        return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken());
+            @NonNull InputChannel toChannel, boolean isDragDrop) {
+        return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken(),
+                isDragDrop);
     }
 
     /**
@@ -1752,7 +1754,8 @@
             @NonNull IBinder toChannelToken) {
         Objects.nonNull(fromChannelToken);
         Objects.nonNull(toChannelToken);
-        return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken);
+        return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken,
+                false /* isDragDrop */);
     }
 
     @Override // Binder call
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a89cb55..e9c3ec3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2381,9 +2381,12 @@
             showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
                     SoftInputShowHideReason.ATTACH_NEW_INPUT);
         }
+        final InputMethodInfo curInputMethodInfo = mMethodMap.get(mCurId);
+        final boolean suppressesSpellChecker =
+                curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
         return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
                 session.session, (session.channel != null ? session.channel.dup() : null),
-                mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
+                mCurId, mCurSeq, mCurActivityViewToScreenMatrix, suppressesSpellChecker);
     }
 
     @Nullable
@@ -2425,7 +2428,7 @@
             // party code.
             return new InputBindResult(
                     InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
-                    null, null, mCurMethodId, mCurSeq, null);
+                    null, null, mCurMethodId, mCurSeq, null, false);
         }
 
         if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2501,7 +2504,7 @@
                     requestClientSessionLocked(cs);
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
-                            null, null, mCurId, mCurSeq, null);
+                            null, null, mCurId, mCurSeq, null, false);
                 } else if (SystemClock.uptimeMillis()
                         < (mLastBindTime+TIME_TO_RECONNECT)) {
                     // In this case we have connected to the service, but
@@ -2513,7 +2516,7 @@
                     // to see if we can get back in touch with the service.
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                            null, null, mCurId, mCurSeq, null);
+                            null, null, mCurId, mCurSeq, null, false);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -2553,7 +2556,7 @@
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                    null, null, mCurId, mCurSeq, null);
+                    null, null, mCurId, mCurSeq, null, false);
         }
         mCurIntent = null;
         Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -2621,9 +2624,8 @@
                 }
                 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
                 // Dispatch display id for InputMethodService to update context display.
-                executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
-                        MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
-                        mMethodMap.get(mCurMethodId).getConfigChanges()));
+                executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
+                        MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
                 scheduleNotifyImeUidToAudioService(mCurMethodUid);
                 if (mCurClient != null) {
                     clearClientSessionLocked(mCurClient);
@@ -3510,7 +3512,7 @@
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
-                    null, null, null, -1, null);
+                    null, null, null, -1, null, false);
         }
 
         mCurFocusedWindow = windowToken;
@@ -4479,8 +4481,7 @@
                     }
                     final IBinder token = (IBinder) args.arg2;
                     ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
-                            new InputMethodPrivilegedOperationsImpl(this, token),
-                            (int) args.arg3);
+                            new InputMethodPrivilegedOperationsImpl(this, token));
                 } catch (RemoteException e) {
                 }
                 args.recycle();
@@ -5838,7 +5839,22 @@
     @BinderThread
     @ShellCommandResult
     private int handleShellCommandTraceInputMethod(@NonNull ShellCommand shellCommand) {
-        int result = ImeTracing.getInstance().onShellCommand(shellCommand);
+        final String cmd = shellCommand.getNextArgRequired();
+        final PrintWriter pw = shellCommand.getOutPrintWriter();
+        switch (cmd) {
+            case "start":
+                ImeTracing.getInstance().getInstance().startTrace(pw);
+                break;
+            case "stop":
+                ImeTracing.getInstance().stopTrace(pw);
+                break;
+            default:
+                pw.println("Unknown command: " + cmd);
+                pw.println("Input method trace options:");
+                pw.println("  start: Start tracing");
+                pw.println("  stop: Stop tracing");
+                return ShellCommandResult.FAILURE;
+        }
         boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
         ArrayMap<IBinder, ClientState> clients;
         synchronized (mMethodMap) {
@@ -5854,7 +5870,7 @@
                 }
             }
         }
-        return result;
+        return ShellCommandResult.SUCCESS;
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1dd3d41..ef1489b 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1748,7 +1748,7 @@
                         return new InputBindResult(
                                 InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
                                 null, null, data.mCurrentInputMethodInfo.getId(),
-                                clientInfo.mBindingSequence, null);
+                                clientInfo.mBindingSequence, null, false);
                     case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
                     case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
                         clientInfo.mBindingSequence++;
@@ -1770,7 +1770,7 @@
                                 clientInfo.mInputMethodSession,
                                 clientInfo.mWriteChannel.dup(),
                                 data.mCurrentInputMethodInfo.getId(),
-                                clientInfo.mBindingSequence, null);
+                                clientInfo.mBindingSequence, null, false);
                     case InputMethodClientState.UNREGISTERED:
                         Slog.e(TAG, "The client is already unregistered.");
                         return InputBindResult.INVALID_CLIENT;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index dde45c4..c44089b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -17,13 +17,16 @@
 package com.android.server.location.contexthub;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -59,6 +62,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
 import com.android.server.location.ContextHubServiceProto;
 
 import java.io.FileDescriptor;
@@ -127,6 +131,8 @@
     // Lock object for sendWifiSettingUpdate()
     private final Object mSendWifiSettingUpdateLock = new Object();
 
+    private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
+
     /**
      * Class extending the callback to register with a Context Hub.
      */
@@ -186,6 +192,7 @@
         if (mContextHubWrapper == null) {
             mTransactionManager = null;
             mClientManager = null;
+            mSensorPrivacyManagerInternal = null;
             mDefaultClientMap = Collections.emptyMap();
             mContextHubIdToInfoMap = Collections.emptyMap();
             mSupportedContextHubPerms = Collections.emptyList();
@@ -208,6 +215,8 @@
         mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
         mTransactionManager = new ContextHubTransactionManager(
                 mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager);
+        mSensorPrivacyManagerInternal =
+                LocalServices.getService(SensorPrivacyManagerInternal.class);
 
         HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
         for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
@@ -284,18 +293,16 @@
         }
 
         if (mContextHubWrapper.supportsMicrophoneDisableSettingNotifications()) {
-            sendMicrophoneDisableSettingUpdate();
+            sendMicrophoneDisableSettingUpdateForCurrentUser();
 
-            SensorPrivacyManager.OnSensorPrivacyChangedListener listener =
-                    new SensorPrivacyManager.OnSensorPrivacyChangedListener() {
-                    @Override
-                    public void onSensorPrivacyChanged(boolean enabled) {
-                        sendMicrophoneDisableSettingUpdate();
-                    }
-                };
-            SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
-            manager.addSensorPrivacyListener(
-                    SensorPrivacyManager.Sensors.MICROPHONE, listener);
+            mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
+                    SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
+                        if (userId == getCurrentUserId()) {
+                            Log.d(TAG, "User: " + userId + " enabled: " + enabled);
+                            sendMicrophoneDisableSettingUpdate(enabled);
+                        }
+                });
+
         }
     }
 
@@ -1074,19 +1081,48 @@
     }
 
     /**
-     * Obtains the latest microphone disable setting value and notifies the
-     * Context Hub.
+     * Notifies a microphone disable settings change to the Context Hub.
      */
-    private void sendMicrophoneDisableSettingUpdate() {
-        SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
-        boolean disabled = manager.isSensorPrivacyEnabled(
-                SensorPrivacyManager.Sensors.MICROPHONE);
-        Log.d(TAG, "Mic Disabled Setting: " + disabled);
-        mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
+    private void sendMicrophoneDisableSettingUpdate(boolean enabled) {
+        Log.d(TAG, "Mic Disabled Setting: " + enabled);
+        mContextHubWrapper.onMicrophoneDisableSettingChanged(enabled);
+    }
+
+    /**
+     * Obtains the latest microphone disabled setting for the current user
+     * and notifies the Context Hub.
+     */
+    private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
+        boolean isEnabled = mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
+                getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
+        sendMicrophoneDisableSettingUpdate(isEnabled);
     }
 
 
     private String getCallingPackageName() {
         return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
     }
+
+    private int getCurrentUserId() {
+        final long id = Binder.clearCallingIdentity();
+        try {
+            UserInfo currentUser = ActivityManager.getService().getCurrentUser();
+            return currentUser.id;
+        } catch (RemoteException e) {
+            // Activity manager not running, nothing we can do - assume user 0.
+        } finally {
+            Binder.restoreCallingIdentity(id);
+        }
+        return UserHandle.USER_SYSTEM;
+    }
+
+    /**
+     * Send a microphone disable settings update whenever the foreground user changes.
+     * We always send a settings update regardless of the previous state for the same user
+     * since the CHRE framework is expected to handle repeated identical setting update.
+     */
+    public void onUserChanged() {
+        Log.d(TAG, "User changed to id: " + getCurrentUserId());
+        sendMicrophoneDisableSettingUpdateForCurrentUser();
+    }
 }
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 5a90fa7..7f47805 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -370,6 +370,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REQUEST_GEOFENCE,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 /* LocationRequest= */ null,
                 /* hasListener= */ false,
@@ -383,6 +384,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REQUEST_GEOFENCE,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 /* LocationRequest= */ null,
                 /* hasListener= */ false,
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index b3119d7..8460d67 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -189,6 +189,7 @@
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
@@ -202,6 +203,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 1eb1618..936283d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -83,6 +83,7 @@
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
@@ -96,6 +97,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index f275663..1eef0de 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -27,6 +27,7 @@
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
 import android.location.Location;
+import android.os.Binder;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -921,6 +922,7 @@
 
     @NativeEntryPoint
     void reportGnssServiceDied() {
+        // Not necessary to clear (and restore) binder identity since it runs on another thread.
         Log.e(TAG, "gnss hal died - restarting shortly...");
 
         // move to another thread just in case there is some awkward gnss thread dependency with
@@ -940,96 +942,111 @@
 
     @NativeEntryPoint
     void reportLocation(boolean hasLatLong, Location location) {
-        if (hasLatLong && !mHasFirstFix) {
-            mHasFirstFix = true;
+        Binder.withCleanCallingIdentity(() -> {
+            if (hasLatLong && !mHasFirstFix) {
+                mHasFirstFix = true;
 
-            // notify status listeners
-            int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
-            for (int i = 0; i < mStatusCallbacks.length; i++) {
-                mStatusCallbacks[i].onReportFirstFix(ttff);
+                // notify status listeners
+                int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
+                for (int i = 0; i < mStatusCallbacks.length; i++) {
+                    mStatusCallbacks[i].onReportFirstFix(ttff);
+                }
             }
-        }
 
-        if (location.hasSpeed()) {
-            boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
-            if (!mItarSpeedLimitExceeded && exceeded) {
-                Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
-            } else if (mItarSpeedLimitExceeded && !exceeded) {
-                Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+            if (location.hasSpeed()) {
+                boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
+                if (!mItarSpeedLimitExceeded && exceeded) {
+                    Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
+                } else if (mItarSpeedLimitExceeded && !exceeded) {
+                    Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+                }
+                mItarSpeedLimitExceeded = exceeded;
             }
-            mItarSpeedLimitExceeded = exceeded;
-        }
 
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mLocationCallbacks.length; i++) {
-            mLocationCallbacks[i].onReportLocation(hasLatLong, location);
-        }
+            for (int i = 0; i < mLocationCallbacks.length; i++) {
+                mLocationCallbacks[i].onReportLocation(hasLatLong, location);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportStatus(@StatusCallbacks.GnssStatusValue int gnssStatus) {
-        for (int i = 0; i < mStatusCallbacks.length; i++) {
-            mStatusCallbacks[i].onReportStatus(gnssStatus);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            for (int i = 0; i < mStatusCallbacks.length; i++) {
+                mStatusCallbacks[i].onReportStatus(gnssStatus);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
             float[] elevations, float[] azimuths, float[] carrierFrequencies,
             float[] basebandCn0DbHzs) {
-        GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
-                azimuths, carrierFrequencies, basebandCn0DbHzs);
-        for (int i = 0; i < mSvStatusCallbacks.length; i++) {
-            mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
+                    azimuths, carrierFrequencies, basebandCn0DbHzs);
+            for (int i = 0; i < mSvStatusCallbacks.length; i++) {
+                mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
-        mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
+        Binder.withCleanCallingIdentity(
+                () -> mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr));
     }
 
     @NativeEntryPoint
     void reportNmea(long timestamp) {
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mNmeaCallbacks.length; i++) {
-            mNmeaCallbacks[i].onReportNmea(timestamp);
-        }
+            for (int i = 0; i < mNmeaCallbacks.length; i++) {
+                mNmeaCallbacks[i].onReportNmea(timestamp);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportMeasurementData(GnssMeasurementsEvent event) {
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mMeasurementCallbacks.length; i++) {
-            mMeasurementCallbacks[i].onReportMeasurements(event);
-        }
+            for (int i = 0; i < mMeasurementCallbacks.length; i++) {
+                mMeasurementCallbacks[i].onReportMeasurements(event);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-        for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
-            mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
+                mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportNavigationMessage(GnssNavigationMessage event) {
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
-            mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
-        }
+            for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
+                mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
+            }
+        });
     }
 
     @NativeEntryPoint
@@ -1061,15 +1078,17 @@
 
     private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
             GnssCapabilities newCapabilities) {
-        if (newCapabilities.equals(oldCapabilities)) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (newCapabilities.equals(oldCapabilities)) {
+                return;
+            }
 
-        Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
+            Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
 
-        for (int i = 0; i < mBaseCallbacks.length; i++) {
-            mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
-        }
+            for (int i = 0; i < mBaseCallbacks.length; i++) {
+                mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
+            }
+        });
     }
 
     @NativeEntryPoint
@@ -1089,88 +1108,103 @@
 
     @NativeEntryPoint
     void reportLocationBatch(Location[] locations) {
-        for (int i = 0; i < mLocationCallbacks.length; i++) {
-            mLocationCallbacks[i].onReportLocations(locations);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            for (int i = 0; i < mLocationCallbacks.length; i++) {
+                mLocationCallbacks[i].onReportLocations(locations);
+            }
+        });
     }
 
     @NativeEntryPoint
     void psdsDownloadRequest(int psdsType) {
-        mPsdsCallbacks.onRequestPsdsDownload(psdsType);
+        Binder.withCleanCallingIdentity(() -> mPsdsCallbacks.onRequestPsdsDownload(psdsType));
     }
 
     @NativeEntryPoint
     void reportGeofenceTransition(int geofenceId, Location location, int transition,
             long transitionTimestamp) {
-        mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, transition,
-                transitionTimestamp);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location,
+                        transition, transitionTimestamp));
     }
 
     @NativeEntryPoint
     void reportGeofenceStatus(int status, Location location) {
-        mGeofenceCallbacks.onReportGeofenceStatus(status, location);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceStatus(status, location));
     }
 
     @NativeEntryPoint
     void reportGeofenceAddStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportGeofenceRemoveStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportGeofencePauseStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportGeofenceResumeStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportNiNotification(int notificationId, int niType, int notifyFlags,
             int timeout, int defaultResponse, String requestorId, String text,
             int requestorIdEncoding, int textEncoding) {
-        mNotificationCallbacks.onReportNiNotification(notificationId, niType, notifyFlags, timeout,
-                    defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
+        Binder.withCleanCallingIdentity(
+                () -> mNotificationCallbacks.onReportNiNotification(notificationId, niType,
+                        notifyFlags, timeout, defaultResponse, requestorId, text,
+                        requestorIdEncoding, textEncoding));
     }
 
     @NativeEntryPoint
     void requestSetID(int flags) {
-        mAGpsCallbacks.onRequestSetID(flags);
+        Binder.withCleanCallingIdentity(() -> mAGpsCallbacks.onRequestSetID(flags));
     }
 
     @NativeEntryPoint
     void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
-        mLocationRequestCallbacks.onRequestLocation(independentFromGnss, isUserEmergency);
+        Binder.withCleanCallingIdentity(
+                () -> mLocationRequestCallbacks.onRequestLocation(independentFromGnss,
+                        isUserEmergency));
     }
 
     @NativeEntryPoint
     void requestUtcTime() {
-        mTimeCallbacks.onRequestUtcTime();
+        Binder.withCleanCallingIdentity(() -> mTimeCallbacks.onRequestUtcTime());
     }
 
     @NativeEntryPoint
     void requestRefLocation() {
-        mLocationRequestCallbacks.onRequestRefLocation();
+        Binder.withCleanCallingIdentity(
+                () -> mLocationRequestCallbacks.onRequestRefLocation());
     }
 
     @NativeEntryPoint
     void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
             String otherProtocolStackName, byte requestor, String requestorId,
             byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
-        mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, protocolStack,
-                    otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
-                    isCachedLocation);
+        Binder.withCleanCallingIdentity(
+                () -> mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName,
+                        protocolStack, otherProtocolStackName, requestor, requestorId, responseType,
+                        inEmergencyMode, isCachedLocation));
     }
 
     @NativeEntryPoint
     boolean isInEmergencySession() {
-        return mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec());
+        return Binder.withCleanCallingIdentity(
+                () -> mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec()));
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
index 244a8e0..af21bcb 100644
--- a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
@@ -49,9 +49,9 @@
      * Log a location API usage event.
      */
     public void logLocationApiUsage(int usageType, int apiInUse,
-            String packageName, String provider, LocationRequest locationRequest,
-            boolean hasListener, boolean hasIntent,
-            Geofence geofence, boolean foreground) {
+            String packageName, String attributionTag, String provider,
+            LocationRequest locationRequest, boolean hasListener,
+            boolean hasIntent, Geofence geofence, boolean foreground) {
         try {
             if (hitApiUsageLogCap()) {
                 return;
@@ -84,7 +84,8 @@
                     isGeofenceNull
                         ? LocationStatsEnums.RADIUS_UNKNOWN
                         : bucketizeRadius(geofence.getRadius()),
-                    categorizeActivityImportance(foreground));
+                    categorizeActivityImportance(foreground),
+                    attributionTag);
         } catch (Exception e) {
             // Swallow exceptions to avoid crashing LMS.
             Log.w(TAG, "Failed to log API usage to statsd.", e);
@@ -114,7 +115,8 @@
                             /* isListenerNull= */ true,
                             /* isIntentNull= */ true),
                     /* bucketizedRadius= */ 0,
-                    LocationStatsEnums.IMPORTANCE_UNKNOWN);
+                    LocationStatsEnums.IMPORTANCE_UNKNOWN,
+                    /* attribution_tag */ null);
         } catch (Exception e) {
             Log.w(TAG, "Failed to log API usage to statsd.", e);
         }
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 2aa6f28..dc8b1d0 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1854,6 +1854,7 @@
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 mName,
                 registration.getRequest(),
                 key instanceof PendingIntent,
@@ -1882,6 +1883,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 mName,
                 registration.getRequest(),
                 key instanceof PendingIntent,
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 240464a..6e99cba 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -64,6 +64,8 @@
     @VisibleForTesting
     public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count";
 
+    static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp";
+
     /**
      * Number of boots until we consider the escrow data to be stale for the purposes of metrics.
      * <p>
@@ -144,8 +146,7 @@
 
         private RebootEscrowProviderInterface createRebootEscrowProvider() {
             RebootEscrowProviderInterface rebootEscrowProvider;
-            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
-                    "server_based_ror_enabled", false)) {
+            if (serverBasedResumeOnReboot()) {
                 Slog.i(TAG, "Using server based resume on reboot");
                 rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
             } else {
@@ -166,6 +167,11 @@
             handler.postDelayed(runnable, delayMillis);
         }
 
+        public boolean serverBasedResumeOnReboot() {
+            return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_ror_enabled", false);
+        }
+
         public Context getContext() {
             return mContext;
         }
@@ -204,10 +210,12 @@
                     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,
-                    -1, 0, -1);
+        public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+                int escrowDurationInSeconds, int vbmetaDigestStatus,
+                int durationSinceBootCompleteInSeconds) {
+            FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success,
+                    errorCode, serviceType, attemptCount, escrowDurationInSeconds,
+                    vbmetaDigestStatus, durationSinceBootCompleteInSeconds);
         }
 
         public RebootEscrowEventLog getEventLog() {
@@ -230,7 +238,7 @@
         mKeyStoreManager = injector.getKeyStoreManager();
     }
 
-    private void onGetRebootEscrowKeyFailed(List<UserInfo> users) {
+    private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) {
         Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
         for (UserInfo user : users) {
             mStorage.removeRebootEscrow(user.id);
@@ -238,7 +246,7 @@
 
         // Clear the old key in keystore.
         mKeyStoreManager.clearKeyStoreEncryptionKey();
-        onEscrowRestoreComplete(false);
+        onEscrowRestoreComplete(false, attemptCount);
     }
 
     void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
@@ -276,7 +284,7 @@
         }
 
         Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
-        onGetRebootEscrowKeyFailed(users);
+        onGetRebootEscrowKeyFailed(users, attemptNumber);
     }
 
     void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
@@ -299,7 +307,7 @@
         }
 
         if (escrowKey == null) {
-            onGetRebootEscrowKeyFailed(users);
+            onGetRebootEscrowKeyFailed(users, attemptNumber + 1);
             return;
         }
 
@@ -313,16 +321,35 @@
         // Clear the old key in keystore. A new key will be generated by new RoR requests.
         mKeyStoreManager.clearKeyStoreEncryptionKey();
 
-        onEscrowRestoreComplete(allUsersUnlocked);
+        onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1);
     }
 
-    private void onEscrowRestoreComplete(boolean success) {
+    private void reportMetricOnRestoreComplete(boolean success, int attemptCount) {
+        int serviceType = mInjector.serverBasedResumeOnReboot()
+                ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED
+                : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL;
+
+        long armedTimestamp = mStorage.getLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, -1,
+                USER_SYSTEM);
+        mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM);
+        int escrowDurationInSeconds = armedTimestamp != -1
+                ? (int) (System.currentTimeMillis() - armedTimestamp) / 1000 : -1;
+
+        // TODO(b/179105110) design error code; and report the true value for other fields.
+        int vbmetaDigestStatus = FrameworkStatsLog
+                .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT;
+
+        mInjector.reportMetric(success, 0 /* error code */, serviceType, attemptCount,
+                escrowDurationInSeconds, vbmetaDigestStatus, -1);
+    }
+
+    private void onEscrowRestoreComplete(boolean success, int attemptCount) {
         int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
         mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
 
         int bootCountDelta = mInjector.getBootCount() - previousBootCount;
         if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
-            mInjector.reportMetric(success);
+            reportMetricOnRestoreComplete(success, attemptCount);
         }
     }
 
@@ -478,6 +505,8 @@
         boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
         if (armedRebootEscrow) {
             mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
+            mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, System.currentTimeMillis(),
+                    USER_SYSTEM);
             mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
         }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
index 35571f1..e75aae1 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
@@ -16,7 +16,7 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import android.security.keystore.AndroidKeyStoreSecretKey;
+import javax.crypto.SecretKey;
 
 /**
  * Used to unwrap recoverable keys before syncing them with remote storage.
@@ -30,7 +30,7 @@
 public class PlatformDecryptionKey {
 
     private final int mGenerationId;
-    private final AndroidKeyStoreSecretKey mKey;
+    private final SecretKey mKey;
 
     /**
      * A new instance.
@@ -40,7 +40,7 @@
      *
      * @hide
      */
-    public PlatformDecryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+    public PlatformDecryptionKey(int generationId, SecretKey key) {
         mGenerationId = generationId;
         mKey = key;
     }
@@ -59,7 +59,7 @@
      *
      * @hide
      */
-    public AndroidKeyStoreSecretKey getKey() {
+    public SecretKey getKey() {
         return mKey;
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
index 38f5b45..ee33446 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
@@ -16,7 +16,7 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import android.security.keystore.AndroidKeyStoreSecretKey;
+import javax.crypto.SecretKey;
 
 /**
  * Private key stored in AndroidKeyStore. Used to wrap recoverable keys before writing them to disk.
@@ -33,7 +33,7 @@
 public class PlatformEncryptionKey {
 
     private final int mGenerationId;
-    private final AndroidKeyStoreSecretKey mKey;
+    private final SecretKey mKey;
 
     /**
      * A new instance.
@@ -41,7 +41,7 @@
      * @param generationId The generation ID of the key.
      * @param key The secret key handle. Can be used to encrypt WITHOUT requiring screen unlock.
      */
-    public PlatformEncryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+    public PlatformEncryptionKey(int generationId, SecretKey key) {
         mGenerationId = generationId;
         mKey = key;
     }
@@ -56,7 +56,7 @@
     /**
      * Returns the actual key, which can only be used to encrypt.
      */
-    public AndroidKeyStoreSecretKey getKey() {
+    public SecretKey getKey() {
         return mKey;
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index f448a6e..f32af54 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -21,7 +21,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.security.GateKeeper;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
@@ -237,7 +236,7 @@
         if (!isKeyLoaded(userId, generationId)) {
             throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
         }
-        AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+        SecretKey key = (SecretKey) mKeyStore.getKey(
                 alias, /*password=*/ null);
         return new PlatformEncryptionKey(generationId, key);
     }
@@ -289,7 +288,7 @@
         if (!isKeyLoaded(userId, generationId)) {
             throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
         }
-        AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+        SecretKey key = (SecretKey) mKeyStore.getKey(
                 alias, /*password=*/ null);
         return new PlatformDecryptionKey(generationId, key);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0cc9f9e..d5a9e3c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7376,15 +7376,7 @@
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
                         if (mNotificationsByKey.get(record.getKey()) != null) {
-                            // Vibrator checks the appops for the op package, not the caller,
-                            // so we need to add the bypass dnd flag to be heard. it's ok to
-                            // always add this flag here because we've already checked that we can
-                            // bypass dnd
-                            AudioAttributes.Builder aab =
-                                    new AudioAttributes.Builder(record.getAudioAttributes())
-                                    .setFlags(FLAG_BYPASS_INTERRUPTION_POLICY);
-                            mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
-                                    effect, "Notification (delayed)", aab.build());
+                            vibrate(record, effect, true);
                         } else {
                             Slog.e(TAG, "No vibration for canceled notification : "
                                     + record.getKey());
@@ -7392,8 +7384,7 @@
                     }
                 }).start();
             } else {
-                mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(),
-                        effect, "Notification", record.getAudioAttributes());
+                vibrate(record, effect, false);
             }
             return true;
         } finally{
@@ -7401,6 +7392,16 @@
         }
     }
 
+    private void vibrate(NotificationRecord record, VibrationEffect effect, boolean delayed) {
+        // We need to vibrate as "android" so we can breakthrough DND. VibratorManagerService
+        // doesn't have a concept of vibrating on an app's behalf, so add the app information
+        // to the reason so we can still debug from bugreports
+        String reason = "Notification (" + record.getSbn().getOpPkg() + " "
+                + record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
+        mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
+                effect, reason, record.getAudioAttributes());
+    }
+
     private boolean isNotificationForCurrentUser(NotificationRecord record) {
         final int currentUser;
         final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 1acbabd..ca8202f 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -105,6 +105,12 @@
     private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
 
     /**
+     * A mapping from the set of App IDs that query other App IDs via library name to the
+     * list of packages that they can see.
+     */
+    private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>();
+
+    /**
      * Executor for running reasonably short background tasks such as building the initial
      * visibility cache.
      */
@@ -239,6 +245,7 @@
         Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable);
         Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage);
         Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent);
+        Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary);
         mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
         mForceQueryable.addAll(orig.mForceQueryable);
         mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames;
@@ -508,6 +515,22 @@
         return false;
     }
 
+    private static boolean canQueryViaUsesLibrary(AndroidPackage querying,
+            AndroidPackage potentialTarget) {
+        if (potentialTarget.getLibraryNames().isEmpty()) {
+            return false;
+        }
+        final List<String> libNames = potentialTarget.getLibraryNames();
+        for (int i = 0, size = libNames.size(); i < size; i++) {
+            final String libName = libNames.get(i);
+            if (querying.getUsesLibraries().contains(libName)
+                    || querying.getUsesOptionalLibraries().contains(libName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static boolean matchesProviders(
             Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
         for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
@@ -707,6 +730,9 @@
                         || canQueryAsInstaller(existingSetting, newPkg)) {
                     mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
                 }
+                if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+                    mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId);
+                }
             }
             // now we'll evaluate our new package's ability to see existing packages
             if (!mForceQueryable.contains(existingSetting.appId)) {
@@ -718,6 +744,9 @@
                         || canQueryAsInstaller(newPkgSetting, existingPkg)) {
                     mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
                 }
+                if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+                    mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId);
+                }
             }
             // if either package instruments the other, mark both as visible to one another
             if (newPkgSetting.pkg != null && existingSetting.pkg != null
@@ -1035,6 +1064,10 @@
             for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
                 mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
             }
+            mQueryableViaUsesLibrary.remove(setting.appId);
+            for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
+                mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId);
+            }
 
             mForceQueryable.remove(setting.appId);
 
@@ -1315,6 +1348,18 @@
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
 
+            try {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
+                if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "queryable for library users");
+                    }
+                    return false;
+                }
+            } finally {
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            }
+
             return true;
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -1394,6 +1439,8 @@
                     filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
                     mImplicitlyQueryable, "      ", expandPackages);
         }
+        pw.println("  queryable via uses-library:");
+        dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, "    ", expandPackages);
     }
 
     private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId,
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index b34611b..29322e2 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -180,6 +180,8 @@
             mId = id;
             mListener = listener;
             mDataLoader = null;
+
+            callListener(IDataLoaderStatusListener.DATA_LOADER_BINDING);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0a443f3..7aaab0c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -51,6 +51,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.storage.StorageManager;
@@ -62,6 +63,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.ArtStatsLogUtils;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.DexoptUtils;
@@ -99,6 +102,8 @@
     private final PowerManager.WakeLock mDexoptWakeLock;
     private volatile boolean mSystemReady;
 
+    private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
+
     PackageDexOptimizer(Installer installer, Object installLock, Context context,
             String wakeLockTag) {
         this.mInstaller = installer;
@@ -252,6 +257,28 @@
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
                         packageStats, options.isDowngrade(), profileName, dexMetadataPath,
                         options.getCompilationReason());
+
+                // Only report metrics for base apk for now.
+                // TODO: add ISA and APK type to metrics.
+                if (pkg.getBaseApkPath().equals(path)) {
+                    Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
+                    try {
+                        long sessionId = Math.randomLongInternal();
+                        ArtStatsLogUtils.writeStatsLog(
+                                mArtStatsLogger,
+                                sessionId,
+                                path,
+                                compilerFilter,
+                                sharedGid,
+                                packageStats.getCompileTime(path),
+                                dexMetadataPath,
+                                options.getCompilationReason(),
+                                newResult);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
+                    }
+                }
+
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 58e2aa2..bafe987 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1011,8 +1011,9 @@
                         "DataLoader installation of APEX modules is not allowed.");
             }
 
-            if (this.params.dataLoaderParams.getComponentName().getPackageName()
-                    == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission(
+            boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+                    this.params.dataLoaderParams.getComponentName().getPackageName());
+            if (systemDataLoader && mContext.checkCallingOrSelfPermission(
                     Manifest.permission.USE_SYSTEM_DATA_LOADERS)
                     != PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException("You need the "
@@ -3060,7 +3061,7 @@
         // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on
         // supported on older devices.
         maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile,
-                VerityUtils.isFsVeritySupported() && DexMetadataHelper.isFsVerityRequired());
+                DexMetadataHelper.isFsVerityRequired());
     }
 
     private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
@@ -3653,6 +3654,7 @@
             @Override
             public void onStatusChanged(int dataLoaderId, int status) {
                 switch (status) {
+                    case IDataLoaderStatusListener.DATA_LOADER_BINDING:
                     case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
                     case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
                         return;
@@ -3763,8 +3765,8 @@
             healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
             healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
 
-            final boolean systemDataLoader =
-                    params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
+            final boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+                    params.getComponentName().getPackageName());
 
             final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
                 @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ff87f1c..dfe72b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -410,6 +410,7 @@
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -892,7 +893,7 @@
     // that created the isolated process.
     @Watched
     @GuardedBy("mLock")
-    final SparseIntArray mIsolatedOwners = new SparseIntArray();
+    final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
 
     /**
      * Tracks new system packages [received in an OTA] that we expect to
@@ -1795,7 +1796,7 @@
         public static final int SNAPPED = 2;
 
         public final Settings settings;
-        public final SparseIntArray isolatedOwners;
+        public final WatchedSparseIntArray isolatedOwners;
         public final WatchedArrayMap<String, AndroidPackage> packages;
         public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs;
         public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs;
@@ -1814,7 +1815,7 @@
         Snapshot(int type) {
             if (type == Snapshot.SNAPPED) {
                 settings = mSettings.snapshot();
-                isolatedOwners = mIsolatedOwners.clone();
+                isolatedOwners = mIsolatedOwners.snapshot();
                 packages = mPackages.snapshot();
                 sharedLibs = mSharedLibraries.snapshot();
                 staticLibs = mStaticLibsByDeclaringPackage.snapshot();
@@ -2016,7 +2017,7 @@
         // Cached attributes.  The names in this class are the same as the
         // names in PackageManagerService; see that class for documentation.
         private final Settings mSettings;
-        private final SparseIntArray mIsolatedOwners;
+        private final WatchedSparseIntArray mIsolatedOwners;
         private final WatchedArrayMap<String, AndroidPackage> mPackages;
         private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
                 mInstrumentation;
@@ -3551,7 +3552,7 @@
         public String getInstantAppPackageName(int callingUid) {
             // If the caller is an isolated app use the owner's uid for the lookup.
             if (Process.isIsolated(callingUid)) {
-                callingUid = mIsolatedOwners.get(callingUid);
+                callingUid = getIsolatedOwner(callingUid);
             }
             final int appId = UserHandle.getAppId(callingUid);
             final Object obj = mSettings.getSettingLPr(appId);
@@ -3563,6 +3564,19 @@
             return null;
         }
 
+        /**
+         * Finds the owner for the provided isolated UID. Throws IllegalStateException if no such
+         * isolated UID is found.
+         */
+        private int getIsolatedOwner(int isolatedUid) {
+            final int ownerUid = mIsolatedOwners.get(isolatedUid, -1);
+            if (ownerUid == -1) {
+                throw new IllegalStateException(
+                        "No owner UID found for isolated UID " + isolatedUid);
+            }
+            return ownerUid;
+        }
+
         public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
             if (pkg.getStaticSharedLibName() != null) {
                 return pkg.getManifestPackageName();
@@ -3929,7 +3943,7 @@
         public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
                 int callingUid) {
             if (Process.isIsolated(callingUid)) {
-                callingUid = mIsolatedOwners.get(callingUid);
+                callingUid = getIsolatedOwner(callingUid);
             }
             final PackageSetting ps = mSettings.getPackageLPr(packageName);
             final boolean returnAllowed =
@@ -4083,7 +4097,7 @@
                 @Nullable ComponentName component, @ComponentType int componentType, int userId) {
             // if we're in an isolated process, get the real calling UID
             if (Process.isIsolated(callingUid)) {
-                callingUid = mIsolatedOwners.get(callingUid);
+                callingUid = getIsolatedOwner(callingUid);
             }
             final String instantAppPkgName = getInstantAppPackageName(callingUid);
             final boolean callerIsInstantApp = instantAppPkgName != null;
@@ -6164,6 +6178,7 @@
         mAppsFilter.registerObserver(mWatcher);
         mInstantAppRegistry.registerObserver(mWatcher);
         mSettings.registerObserver(mWatcher);
+        mIsolatedOwners.registerObserver(mWatcher);
         // If neither "build" attribute is true then this may be a mockito test, and verification
         // can fail as a false positive.
         Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
@@ -27803,13 +27818,23 @@
     }
 
     private static String getDefaultTimeouts() {
-        return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
-                PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                    PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private static String getKnownDigestersList() {
-        return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
-                PROPERTY_KNOWN_DIGESTERS_LIST, "");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                    PROPERTY_KNOWN_DIGESTERS_LIST, "");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d1cf55d..38cba4c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1889,231 +1889,248 @@
     @Override
     public void setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
             @UserIdInt int userId, @NonNull AndroidFuture callback) {
-        verifyCaller(packageName, userId);
+        try {
+            verifyCaller(packageName, userId);
 
-        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
-        verifyShortcutInfoPackages(packageName, newShortcuts);
-        final int size = newShortcuts.size();
+            final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+            verifyShortcutInfoPackages(packageName, newShortcuts);
+            final int size = newShortcuts.size();
 
-        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
-                injectBinderCallingPid(), injectBinderCallingUid());
+            final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                    injectBinderCallingPid(), injectBinderCallingUid());
 
-        List<ShortcutInfo> changedShortcuts = null;
-        List<ShortcutInfo> removedShortcuts = null;
+            List<ShortcutInfo> changedShortcuts = null;
+            List<ShortcutInfo> removedShortcuts = null;
 
-        synchronized (mLock) {
-            throwIfUserLockedL(userId);
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+                final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName,
+                        userId);
 
-            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
-            ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+                ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+                ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
 
-            fillInDefaultActivity(newShortcuts);
+                fillInDefaultActivity(newShortcuts);
 
-            ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
+                ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
 
-            // Throttling.
-            if (!ps.tryApiCall(unlimited)) {
-                callback.complete(false);
+                // Throttling.
+                if (!ps.tryApiCall(unlimited)) {
+                    callback.complete(false);
+                    return;
+                }
+
+                // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+                ps.clearAllImplicitRanks();
+                assignImplicitRanks(newShortcuts);
+
+                for (int i = 0; i < size; i++) {
+                    fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
+                }
+
+                ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
+                ps.findAll(cachedOrPinned, (ShortcutInfo si) -> si.isVisibleToPublisher()
+                                && si.isDynamic() && (si.isCached() || si.isPinned()),
+                        ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+                // First, remove all un-pinned and non-cached; dynamic shortcuts
+                removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+
+                // Then, add/update all.  We need to make sure to take over "pinned" flag.
+                for (int i = 0; i < size; i++) {
+                    final ShortcutInfo newShortcut = newShortcuts.get(i);
+                    ps.addOrReplaceDynamicShortcut(newShortcut);
+                }
+
+                // Lastly, adjust the ranks.
+                ps.adjustRanks();
+
+                changedShortcuts = prepareChangedShortcuts(
+                        cachedOrPinned, newShortcuts, removedShortcuts, ps);
             }
 
-            // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
-            ps.clearAllImplicitRanks();
-            assignImplicitRanks(newShortcuts);
+            packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
 
-            for (int i = 0; i < size; i++) {
-                fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
-            }
+            verifyStates();
 
-            ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
-            ps.findAll(cachedOrPinned, (ShortcutInfo si) -> si.isVisibleToPublisher()
-                    && si.isDynamic() && (si.isCached() || si.isPinned()),
-                    ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
-
-            // First, remove all un-pinned and non-cached; dynamic shortcuts
-            removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
-
-            // Then, add/update all.  We need to make sure to take over "pinned" flag.
-            for (int i = 0; i < size; i++) {
-                final ShortcutInfo newShortcut = newShortcuts.get(i);
-                ps.addOrReplaceDynamicShortcut(newShortcut);
-            }
-
-            // Lastly, adjust the ranks.
-            ps.adjustRanks();
-
-            changedShortcuts = prepareChangedShortcuts(
-                    cachedOrPinned, newShortcuts, removedShortcuts, ps);
+            callback.complete(true);
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
         }
-
-        packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
-
-        verifyStates();
-
-        callback.complete(true);
     }
 
     @Override
     public void updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
             @UserIdInt int userId, AndroidFuture callback) {
-        verifyCaller(packageName, userId);
+        try {
+            verifyCaller(packageName, userId);
 
-        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
-        verifyShortcutInfoPackages(packageName, newShortcuts);
-        final int size = newShortcuts.size();
+            final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+            verifyShortcutInfoPackages(packageName, newShortcuts);
+            final int size = newShortcuts.size();
 
-        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
-                injectBinderCallingPid(), injectBinderCallingUid());
+            final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                    injectBinderCallingPid(), injectBinderCallingUid());
 
-        final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
+            final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
 
-        synchronized (mLock) {
-            throwIfUserLockedL(userId);
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+                final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName,
+                        userId);
 
-            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
-            ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+                ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+                ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
 
-            // For update, don't fill in the default activity.  Having null activity means
-            // "don't update the activity" here.
+                // For update, don't fill in the default activity.  Having null activity means
+                // "don't update the activity" here.
 
-            ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
+                ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
 
-            // Throttling.
-            if (!ps.tryApiCall(unlimited)) {
-                callback.complete(false);
-                return;
+                // Throttling.
+                if (!ps.tryApiCall(unlimited)) {
+                    callback.complete(false);
+                    return;
+                }
+
+                // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+                ps.clearAllImplicitRanks();
+                assignImplicitRanks(newShortcuts);
+
+                for (int i = 0; i < size; i++) {
+                    final ShortcutInfo source = newShortcuts.get(i);
+                    fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
+
+                    ps.mutateShortcut(source.getId(), null, target -> {
+                        // Invisible shortcuts can't be updated.
+                        if (target == null || !target.isVisibleToPublisher()) {
+                            return;
+                        }
+
+                        if (target.isEnabled() != source.isEnabled()) {
+                            Slog.w(TAG, "ShortcutInfo.enabled cannot be changed with"
+                                    + " updateShortcuts()");
+                        }
+
+                        if (target.isLongLived() != source.isLongLived()) {
+                            Slog.w(TAG,
+                                    "ShortcutInfo.longLived cannot be changed with"
+                                    + " updateShortcuts()");
+                        }
+
+                        // When updating the rank, we need to insert between existing ranks, so set
+                        // this setRankChanged, and also copy the implicit rank fo adjustRanks().
+                        if (source.hasRank()) {
+                            target.setRankChanged();
+                            target.setImplicitRank(source.getImplicitRank());
+                        }
+
+                        final boolean replacingIcon = (source.getIcon() != null);
+                        if (replacingIcon) {
+                            removeIconLocked(target);
+                        }
+
+                        // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
+                        target.copyNonNullFieldsFrom(source);
+                        target.setTimestamp(injectCurrentTimeMillis());
+
+                        if (replacingIcon) {
+                            saveIconAndFixUpShortcutLocked(target);
+                        }
+
+                        // When we're updating any resource related fields, re-extract the res
+                        // names and the values.
+                        if (replacingIcon || source.hasStringResources()) {
+                            fixUpShortcutResourceNamesAndValues(target);
+                        }
+
+                        changedShortcuts.add(target);
+                    });
+                }
+
+                // Lastly, adjust the ranks.
+                ps.adjustRanks();
             }
+            packageShortcutsChanged(packageName, userId,
+                    changedShortcuts.isEmpty() ? null : changedShortcuts, null);
 
-            // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
-            ps.clearAllImplicitRanks();
-            assignImplicitRanks(newShortcuts);
+            verifyStates();
 
-            for (int i = 0; i < size; i++) {
-                final ShortcutInfo source = newShortcuts.get(i);
-                fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
-
-                ps.mutateShortcut(source.getId(), null, target -> {
-                    // Invisible shortcuts can't be updated.
-                    if (target == null || !target.isVisibleToPublisher()) {
-                        return;
-                    }
-
-                    if (target.isEnabled() != source.isEnabled()) {
-                        Slog.w(TAG,
-                                "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
-                    }
-
-                    if (target.isLongLived() != source.isLongLived()) {
-                        Slog.w(TAG,
-                                "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
-                    }
-
-                    // When updating the rank, we need to insert between existing ranks, so set
-                    // this setRankChanged, and also copy the implicit rank fo adjustRanks().
-                    if (source.hasRank()) {
-                        target.setRankChanged();
-                        target.setImplicitRank(source.getImplicitRank());
-                    }
-
-                    final boolean replacingIcon = (source.getIcon() != null);
-                    if (replacingIcon) {
-                        removeIconLocked(target);
-                    }
-
-                    // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
-                    target.copyNonNullFieldsFrom(source);
-                    target.setTimestamp(injectCurrentTimeMillis());
-
-                    if (replacingIcon) {
-                        saveIconAndFixUpShortcutLocked(target);
-                    }
-
-                    // When we're updating any resource related fields, re-extract the res names and
-                    // the values.
-                    if (replacingIcon || source.hasStringResources()) {
-                        fixUpShortcutResourceNamesAndValues(target);
-                    }
-
-                    changedShortcuts.add(target);
-                });
-            }
-
-            // Lastly, adjust the ranks.
-            ps.adjustRanks();
+            callback.complete(true);
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
         }
-        packageShortcutsChanged(packageName, userId,
-                changedShortcuts.isEmpty() ? null : changedShortcuts, null);
-
-        verifyStates();
-
-        callback.complete(true);
     }
 
     @Override
     public void addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
             @UserIdInt int userId, AndroidFuture callback) {
-        verifyCaller(packageName, userId);
+        try {
+            verifyCaller(packageName, userId);
 
-        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
-        verifyShortcutInfoPackages(packageName, newShortcuts);
-        final int size = newShortcuts.size();
+            final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+            verifyShortcutInfoPackages(packageName, newShortcuts);
+            final int size = newShortcuts.size();
 
-        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
-                injectBinderCallingPid(), injectBinderCallingUid());
+            final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                    injectBinderCallingPid(), injectBinderCallingUid());
 
-        List<ShortcutInfo> changedShortcuts = null;
+            List<ShortcutInfo> changedShortcuts = null;
 
-        synchronized (mLock) {
-            throwIfUserLockedL(userId);
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+                final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName,
+                        userId);
 
-            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
-            ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+                ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+                ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
 
-            fillInDefaultActivity(newShortcuts);
+                fillInDefaultActivity(newShortcuts);
 
-            ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
+                ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD);
 
-            // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
-            ps.clearAllImplicitRanks();
-            assignImplicitRanks(newShortcuts);
+                // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+                ps.clearAllImplicitRanks();
+                assignImplicitRanks(newShortcuts);
 
-            // Throttling.
-            if (!ps.tryApiCall(unlimited)) {
-                callback.complete(false);
-                return;
-            }
-            for (int i = 0; i < size; i++) {
-                final ShortcutInfo newShortcut = newShortcuts.get(i);
-
-                // Validate the shortcut.
-                fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
-
-                // When ranks are changing, we need to insert between ranks, so set the
-                // "rank changed" flag.
-                newShortcut.setRankChanged();
-
-                // Add it.
-                ps.addOrReplaceDynamicShortcut(newShortcut);
-
-                if (changedShortcuts == null) {
-                    changedShortcuts = new ArrayList<>(1);
+                // Throttling.
+                if (!ps.tryApiCall(unlimited)) {
+                    callback.complete(false);
+                    return;
                 }
-                changedShortcuts.add(newShortcut);
+                for (int i = 0; i < size; i++) {
+                    final ShortcutInfo newShortcut = newShortcuts.get(i);
+
+                    // Validate the shortcut.
+                    fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
+
+                    // When ranks are changing, we need to insert between ranks, so set the
+                    // "rank changed" flag.
+                    newShortcut.setRankChanged();
+
+                    // Add it.
+                    ps.addOrReplaceDynamicShortcut(newShortcut);
+
+                    if (changedShortcuts == null) {
+                        changedShortcuts = new ArrayList<>(1);
+                    }
+                    changedShortcuts.add(newShortcut);
+                }
+
+                // Lastly, adjust the ranks.
+                ps.adjustRanks();
             }
+            packageShortcutsChanged(packageName, userId, changedShortcuts, null);
 
-            // Lastly, adjust the ranks.
-            ps.adjustRanks();
+            verifyStates();
+
+            callback.complete(true);
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
         }
-        packageShortcutsChanged(packageName, userId, changedShortcuts, null);
-
-        verifyStates();
-
-        callback.complete(true);
     }
 
     @Override
@@ -2180,31 +2197,40 @@
     @Override
     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");
-        callback.complete(requestPinItem(packageName, userId, shortcut, null, null, resultIntent));
+        try {
+            Objects.requireNonNull(shortcut);
+            Objects.requireNonNull(callback);
+            Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+            callback.complete(
+                    requestPinItem(packageName, userId, shortcut, null, null, resultIntent));
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
+        }
     }
 
     @Override
     public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId,
             AndroidFuture callback)
             throws RemoteException {
-        Objects.requireNonNull(shortcut);
-        Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
-        verifyCaller(packageName, userId);
-        verifyShortcutInfoPackage(packageName, shortcut);
+        try {
+            Objects.requireNonNull(shortcut);
+            Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+            verifyCaller(packageName, userId);
+            verifyShortcutInfoPackage(packageName, shortcut);
 
-        final Intent ret;
-        synchronized (mLock) {
-            throwIfUserLockedL(userId);
+            final Intent ret;
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
 
-            // Send request to the launcher, if supported.
-            ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
+                // Send request to the launcher, if supported.
+                ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
+            }
+
+            verifyStates();
+            callback.complete(ret);
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
         }
-
-        verifyStates();
-        callback.complete(ret);
     }
 
     /**
@@ -2464,47 +2490,57 @@
     public void getShortcuts(String packageName,
             @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId,
             AndroidFuture<ParceledListSlice<ShortcutInfo>> callback) {
-        verifyCaller(packageName, userId);
+        try {
+            verifyCaller(packageName, userId);
 
-        synchronized (mLock) {
-            throwIfUserLockedL(userId);
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
 
-            final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
-            final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
-            final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
-            final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
+                final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
+                final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
+                final boolean matchManifest =
+                        (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
+                final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
 
-            final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
-                    | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
-                    | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
-                    | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
+                final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
+                        | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
+                        | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
+                        | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
 
-            callback.complete(getShortcutsWithQueryLocked(
-                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
-                    (ShortcutInfo si) ->
-                            si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0));
+                callback.complete(getShortcutsWithQueryLocked(
+                        packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+                        (ShortcutInfo si) ->
+                                si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0));
+            }
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
         }
     }
 
     @Override
     public void getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId,
             AndroidFuture<ParceledListSlice> callback) {
-        Preconditions.checkStringNotEmpty(packageName, "packageName");
-        Objects.requireNonNull(filter, "intentFilter");
+        try {
+            Preconditions.checkStringNotEmpty(packageName, "packageName");
+            Objects.requireNonNull(filter, "intentFilter");
 
-        verifyCaller(packageName, userId);
-        enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
-                "getShareTargets");
+            verifyCaller(packageName, userId);
+            enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
+                    "getShareTargets");
 
-        synchronized (mLock) {
-            throwIfUserLockedL(userId);
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
 
-            final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
+                final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
 
-            final ShortcutUser user = getUserShortcutsLocked(userId);
-            user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
+                final ShortcutUser user = getUserShortcutsLocked(userId);
+                user.forAllPackages(
+                        p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
 
-            callback.complete(new ParceledListSlice<>(shortcutInfoList));
+                callback.complete(new ParceledListSlice<>(shortcutInfoList));
+            }
+        } catch (Exception e) {
+            callback.completeExceptionally(e);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
new file mode 100644
index 0000000..3b77c39
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+import android.util.jar.StrictJarFile;
+import android.util.Slog;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.PackageManagerService;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+
+/** Utils class to report ART metrics to statsd. */
+public class ArtStatsLogUtils {
+    private static final String TAG = ArtStatsLogUtils.class.getSimpleName();
+    private static final String PROFILE_DEX_METADATA = "primary.prof";
+    private static final String VDEX_DEX_METADATA = "primary.vdex";
+
+
+    private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY =
+            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+    private static final int ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED =
+            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+    private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED =
+            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+
+    private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK =
+            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+    private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK =
+            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+    private static final Map<Integer, Integer> COMPILATION_REASON_MAP = new HashMap();
+
+    static {
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_UNKNOWN, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_FAST, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_FAST);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+                ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+                ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+                ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BACKGROUND_DEXOPT, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BG_DEXOPT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_AB_OTA, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_AB_OTA);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,
+                ArtStatsLog.
+                        ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INACTIVE);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_SHARED,
+                ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_SHARED);
+    }
+
+    private static final Map<String, Integer> COMPILE_FILTER_MAP = new HashMap();
+
+    static {
+        COMPILE_FILTER_MAP.put("error", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR);
+        COMPILE_FILTER_MAP.put("unknown", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN);
+        COMPILE_FILTER_MAP.put("assume-verified", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED);
+        COMPILE_FILTER_MAP.put("extract", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT);
+        COMPILE_FILTER_MAP.put("verify", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY);
+        COMPILE_FILTER_MAP.put("quicken", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_QUICKEN);
+        COMPILE_FILTER_MAP.put("space-profile", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE);
+        COMPILE_FILTER_MAP.put("space", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE);
+        COMPILE_FILTER_MAP.put("speed-profile", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE);
+        COMPILE_FILTER_MAP.put("speed", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED);
+        COMPILE_FILTER_MAP.put("everything-profile", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE);
+        COMPILE_FILTER_MAP.put("everything", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING);
+        COMPILE_FILTER_MAP.put("run-from-apk", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK);
+        COMPILE_FILTER_MAP.put("run-from-apk-fallback",
+                ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK);
+        COMPILE_FILTER_MAP.put("run-from-vdex-fallback",
+                ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK);
+    }
+
+    public static void writeStatsLog(
+            ArtStatsLogger logger,
+            long sessionId,
+            String path,
+            String compilerFilter,
+            int uid,
+            long compileTime,
+            String dexMetadataPath,
+            int compilationReason,
+            int result) {
+        int dexMetadataType = getDexMetadataType(dexMetadataPath);
+        logger.write(
+                sessionId,
+                uid,
+                compilationReason,
+                compilerFilter,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+                result,
+                dexMetadataType);
+        logger.write(
+                sessionId,
+                uid,
+                compilationReason,
+                compilerFilter,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+                getDexBytes(path),
+                dexMetadataType);
+        logger.write(
+                sessionId,
+                uid,
+                compilationReason,
+                compilerFilter,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+                compileTime,
+                dexMetadataType);
+    }
+
+    private static long getDexBytes(String apkPath) {
+        StrictJarFile jarFile = null;
+        long dexBytes = 0;
+        try {
+            jarFile = new StrictJarFile(apkPath,
+                    /*verify=*/ false,
+                    /*signatureSchemeRollbackProtectionsEnforced=*/ false);
+            Iterator<ZipEntry> it = jarFile.iterator();
+            while (it.hasNext()) {
+                ZipEntry entry = it.next();
+                if (entry.getName().matches("classes(\\d)*[.]dex")) {
+                    dexBytes += entry.getSize();
+                }
+            }
+            return dexBytes;
+        } catch (IOException ignore) {
+            Slog.e(TAG, "Error when parsing APK " + apkPath);
+            return -1L;
+        } finally {
+            try {
+                if (jarFile != null) {
+                    jarFile.close();
+                }
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    private static int getDexMetadataType(String dexMetadataPath) {
+        if (dexMetadataPath == null) {
+            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA;
+        }
+        StrictJarFile jarFile = null;
+        try {
+            jarFile = new StrictJarFile(dexMetadataPath,
+                    /*verify=*/ false,
+                    /*signatureSchemeRollbackProtectionsEnforced=*/false);
+            boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA);
+            boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA);
+            if (hasProfile && hasVdex) {
+                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX;
+            } else if (hasProfile) {
+                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE;
+            } else if (hasVdex) {
+                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX;
+            } else {
+                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA;
+            }
+        } catch (IOException ignore) {
+            Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath);
+            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ERROR_DEX_METADATA;
+        } finally {
+            try {
+                if (jarFile != null) {
+                    jarFile.close();
+                }
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    private static boolean findFileName(StrictJarFile jarFile, String filename) throws IOException {
+        Iterator<ZipEntry> it = jarFile.iterator();
+        while (it.hasNext()) {
+            ZipEntry entry = it.next();
+            if (entry.getName().equals(filename)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static class ArtStatsLogger {
+        public void write(
+                long sessionId,
+                int uid,
+                int compilationReason,
+                String compilerFilter,
+                int kind,
+                long value,
+                int dexMetadataType) {
+            ArtStatsLog.write(
+                    ArtStatsLog.ART_DATUM_REPORTED,
+                    sessionId,
+                    uid,
+                    COMPILE_FILTER_MAP.getOrDefault(compilerFilter, ArtStatsLog.
+                            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
+                    COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
+                            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
+                    /*timestamp_millis=*/ 0L,
+                    ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
+                    kind,
+                    value,
+                    dexMetadataType);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e3ccb75..27bf8a13 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -737,10 +737,12 @@
             }
         }
         if (locationExtraPackageNames != null) {
-            // Also grant location permission to location extra packages.
+            // Also grant location and activity recognition permission to location extra packages.
             for (String packageName : locationExtraPackageNames) {
                 grantPermissionsToSystemPackage(pm, packageName, userId,
                         ALWAYS_LOCATION_PERMISSIONS);
+                grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
+                        ACTIVITY_RECOGNITION_PERMISSIONS);
             }
         }
 
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 0c2b4c5..b4bcde7 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
@@ -80,14 +80,14 @@
      * been preserved for migration purposes, but is otherwise ignored. Corresponds to
      * {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS}.
      */
-    int APPROVAL_LEVEL_LEGACY_ALWAYS = 1;
+    int APPROVAL_LEVEL_LEGACY_ALWAYS = 2;
 
     /**
      * The app has been chosen by the user through
      * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
      * indicating an explicit choice to use this app to open an unverified domain.
      */
-    int APPROVAL_LEVEL_SELECTION = 2;
+    int APPROVAL_LEVEL_SELECTION = 3;
 
     /**
      * The app is approved through the digital asset link statement being hosted at the domain
@@ -95,7 +95,7 @@
      * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by
      * the domain verification agent on device.
      */
-    int APPROVAL_LEVEL_VERIFIED = 3;
+    int APPROVAL_LEVEL_VERIFIED = 4;
 
     /**
      * The app has been installed as an instant app, which grants it total authority on the domains
@@ -105,7 +105,7 @@
      * The user is still able to disable instant app link handling through
      * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}.
      */
-    int APPROVAL_LEVEL_INSTANT_APP = 4;
+    int APPROVAL_LEVEL_INSTANT_APP = 5;
 
     /**
      * Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 047e3b3..1e49071 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -345,6 +345,9 @@
      */
     private final Object mLock = new Object();
 
+    /** List of {@link ScreenOnListener}s which do not belong to the default display. */
+    private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>();
+
     Context mContext;
     IWindowManager mWindowManager;
     WindowManagerFuncs mWindowManagerFuncs;
@@ -434,8 +437,25 @@
     volatile boolean mBeganFromNonInteractive;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
-    volatile boolean mGoingToSleep;
-    volatile boolean mRequestedOrGoingToSleep;
+
+    /**
+     * {@code true} if the device is entering a low-power state; {@code false otherwise}.
+     *
+     * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state
+     * of the {@link #mDefaultDisplay default display} versus the power state of the entire device.
+     */
+    volatile boolean mDeviceGoingToSleep;
+
+    /**
+     * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to
+     * enter a low-power state; {@code false otherwise}.
+     *
+     * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire
+     * device versus the power state of the {@link #mDefaultDisplay default display}.
+     */
+    // TODO(b/178103325): Track sleep/requested sleep for every display.
+    volatile boolean mRequestedOrSleepingDefaultDisplay;
+
     volatile boolean mRecentsVisible;
     volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
     volatile boolean mPictureInPictureVisible;
@@ -917,13 +937,14 @@
                 case SHORT_PRESS_POWER_NOTHING:
                     break;
                 case SHORT_PRESS_POWER_GO_TO_SLEEP:
-                    goToSleepFromPowerButton(eventTime, 0);
+                    sleepDefaultDisplayFromPowerButton(eventTime, 0);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
-                    goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+                    sleepDefaultDisplayFromPowerButton(eventTime,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
-                    if (goToSleepFromPowerButton(eventTime,
+                    if (sleepDefaultDisplayFromPowerButton(eventTime,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
                         launchHomeFromHotKey(DEFAULT_DISPLAY);
                     }
@@ -951,11 +972,12 @@
     }
 
     /**
-     * Sends the device to sleep as a result of a power button press.
+     * Sends the default display to sleep as a result of a power button press.
      *
-     * @return True if the was device was sent to sleep, false if sleep was suppressed.
+     * @return {@code true} if the device was sent to sleep, {@code false} if the device did not
+     * sleep.
      */
-    private boolean goToSleepFromPowerButton(long eventTime, int flags) {
+    private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
         // Before we actually go to sleep, we check the last wakeup reason.
         // If the device very recently woke up from a gesture (like user lifting their device)
         // then ignore the sleep instruction. This is because users have developed
@@ -975,12 +997,12 @@
             }
         }
 
-        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
+        sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
         return true;
     }
 
-    private void goToSleep(long eventTime, int reason, int flags) {
-        mRequestedOrGoingToSleep = true;
+    private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
+        mRequestedOrSleepingDefaultDisplay = true;
         mPowerManager.goToSleep(eventTime, reason, flags);
     }
 
@@ -1017,7 +1039,8 @@
                             Settings.Global.THEATER_MODE_ON, 1);
 
                     if (mGoToSleepOnButtonPressTheaterMode && interactive) {
-                        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+                        sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+                                0);
                     }
                 }
                 break;
@@ -1126,7 +1149,7 @@
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
                 Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
-                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+                sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
         }
     }
@@ -2212,9 +2235,9 @@
 
     /** {@inheritDoc} */
     @Override
-    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
-            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
-            int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+    public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+            int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
         if (!SHOW_SPLASH_SCREENS) {
             return null;
         }
@@ -2241,10 +2264,12 @@
 
             if (theme != context.getThemeResId() || labelRes != 0) {
                 try {
-                    context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);
+                    context = context.createPackageContextAsUser(packageName, CONTEXT_RESTRICTED,
+                            UserHandle.of(userId));
                     context.setTheme(theme);
                 } catch (PackageManager.NameNotFoundException e) {
-                    // Ignore
+                    Slog.w(TAG, "Failed creating package context with package name "
+                            + packageName + " for user " + userId, e);
                 }
             }
 
@@ -3511,7 +3536,7 @@
                             }
                             if ((mEndcallBehavior
                                     & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
-                                goToSleep(event.getEventTime(),
+                                sleepDefaultDisplay(event.getEventTime(),
                                         PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                 isWakeKey = false;
                             }
@@ -3538,10 +3563,12 @@
                 // Any activity on the power button stops the accessibility shortcut
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
+                final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+                final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
                 if (down) {
-                    interceptPowerKeyDown(event, interactive);
+                    interceptPowerKeyDown(event, interactiveAndOn);
                 } else {
-                    interceptPowerKeyUp(event, interactive, canceled);
+                    interceptPowerKeyUp(event, interactiveAndOn, canceled);
                 }
                 break;
             }
@@ -3746,7 +3773,7 @@
         final MutableBoolean outLaunched = new MutableBoolean(false);
         final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event,
                 interactive, outLaunched);
-        if (outLaunched.value && mRequestedOrGoingToSleep) {
+        if (outLaunched.value && mRequestedOrSleepingDefaultDisplay) {
             mCameraGestureTriggeredDuringGoingToSleep = true;
         }
         return gesturedServiceIntercepted;
@@ -4088,8 +4115,8 @@
                                     pmSleepReason)) + ")");
         }
 
-        mGoingToSleep = true;
-        mRequestedOrGoingToSleep = true;
+        mDeviceGoingToSleep = true;
+        mRequestedOrSleepingDefaultDisplay = true;
 
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
@@ -4108,8 +4135,8 @@
         }
         MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
 
-        mGoingToSleep = false;
-        mRequestedOrGoingToSleep = false;
+        mDeviceGoingToSleep = false;
+        mRequestedOrSleepingDefaultDisplay = false;
         mDefaultDisplayPolicy.setAwake(false);
 
         // We must get this work done here because the power manager will drop
@@ -4253,21 +4280,25 @@
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
     public void screenTurnedOff(int displayId) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
 
-        if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
-
-        updateScreenOffSleepToken(true);
-        mDefaultDisplayPolicy.screenTurnedOff();
-        synchronized (mLock) {
-            if (mKeyguardDelegate != null) {
-                mKeyguardDelegate.onScreenTurnedOff();
+        if (displayId == DEFAULT_DISPLAY) {
+            updateScreenOffSleepToken(true);
+            mRequestedOrSleepingDefaultDisplay = false;
+            mDefaultDisplayPolicy.screenTurnedOff();
+            synchronized (mLock) {
+                if (mKeyguardDelegate != null) {
+                    mKeyguardDelegate.onScreenTurnedOff();
+                }
+            }
+            mDefaultDisplayRotation.updateOrientationListener();
+            reportScreenStateToVrManager(false);
+            if (mCameraGestureTriggeredDuringGoingToSleep) {
+                wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
+                        PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+                        "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
             }
         }
-        mDefaultDisplayRotation.updateOrientationListener();
-        reportScreenStateToVrManager(false);
     }
 
     private long getKeyguardDrawnTimeout() {
@@ -4280,27 +4311,28 @@
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
     public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");
 
-        if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
+        if (displayId == DEFAULT_DISPLAY) {
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
+                    0 /* cookie */);
+            updateScreenOffSleepToken(false);
+            mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
 
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
-        updateScreenOffSleepToken(false);
-        mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
-
-        synchronized (mLock) {
-            if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
-                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
-                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
-                        getKeyguardDrawnTimeout());
-                mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
-            } else {
-                if (DEBUG_WAKEUP) Slog.d(TAG,
-                        "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
-                mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+            synchronized (mLock) {
+                if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
+                    mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
+                    mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
+                            getKeyguardDrawnTimeout());
+                    mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
+                } else {
+                    if (DEBUG_WAKEUP) Slog.d(TAG,
+                            "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
+                    mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+                }
             }
+        } else {
+            mScreenOnListeners.put(displayId, screenOnListener);
         }
     }
 
@@ -4321,11 +4353,12 @@
 
     @Override
     public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
+        mWindowManagerFuncs.screenTurningOff(displayId, screenOffListener);
         if (displayId != DEFAULT_DISPLAY) {
             return;
         }
 
-        mWindowManagerFuncs.screenTurningOff(screenOffListener);
+        mRequestedOrSleepingDefaultDisplay = true;
         synchronized (mLock) {
             if (mKeyguardDelegate != null) {
                 mKeyguardDelegate.onScreenTurningOff();
@@ -4380,6 +4413,14 @@
             listener.onScreenOn();
         }
 
+        for (int i = mScreenOnListeners.size() - 1; i >= 0; i--) {
+            final ScreenOnListener screenOnListener = mScreenOnListeners.valueAt(i);
+            if (screenOnListener != null) {
+                screenOnListener.onScreenOn();
+            }
+        }
+        mScreenOnListeners.clear();
+
         if (enableScreen) {
             try {
                 mWindowManager.enableScreenIfNeeded();
@@ -4410,7 +4451,7 @@
 
     @Override
     public boolean okToAnimate() {
-        return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
+        return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep;
     }
 
     /** {@inheritDoc} */
@@ -4777,7 +4818,7 @@
                     mWindowManagerFuncs.lockDeviceNow();
                     break;
                 case LID_BEHAVIOR_SLEEP:
-                    goToSleep(SystemClock.uptimeMillis(),
+                    sleepDefaultDisplay(SystemClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
index b9202c3..72933a0 100644
--- a/services/core/java/com/android/server/policy/SplashScreenSurface.java
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -45,7 +45,7 @@
     }
 
     @Override
-    public void remove() {
+    public void remove(boolean animate) {
         if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": "
                         + this + " Callers=" + Debug.getCallers(4));
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b5a9aca..d512edf 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -238,8 +238,9 @@
         /**
          * Removes the starting window surface. Do not hold the window manager lock when calling
          * this method!
+         * @param animate Whether need to play the default exit animation for starting window.
          */
-        void remove();
+        void remove(boolean animate);
     }
 
     /**
@@ -303,9 +304,10 @@
         /**
          * Notifies the window manager that screen is being turned off.
          *
+         * @param displayId the ID of the display which is turning off
          * @param listener callback to call when display can be turned off
          */
-        void screenTurningOff(ScreenOffListener listener);
+        void screenTurningOff(int displayId, ScreenOffListener listener);
 
         /**
          * Convert the lid state to a human readable format.
@@ -712,9 +714,9 @@
      * @return The starting surface.
      *
      */
-    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
-            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
-            int logo, int windowFlags, Configuration overrideConfig, int displayId);
+    StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+            int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId);
 
     /**
      * Set or clear a window which can behave as the keyguard.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index a95628f..44f14b4 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -258,8 +258,12 @@
         }
     }
 
+    /**
+     * @deprecated Notify occlude status change via remote animation.
+     */
+    @Deprecated
     public void setOccluded(boolean isOccluded, boolean animate) {
-        if (mKeyguardService != null) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
             if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
             mKeyguardService.setOccluded(isOccluded, animate);
         }
diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java
index fe9663a..676181d 100644
--- a/services/core/java/com/android/server/power/FaceDownDetector.java
+++ b/services/core/java/com/android/server/power/FaceDownDetector.java
@@ -291,8 +291,10 @@
      * The user interacted with the screen while face down, indicated the phone is in use.
      * We log this event and temporarily make this detector inactive.
      */
-    public void userActivity() {
-        mHandler.post(mUserActivityRunnable);
+    public void userActivity(int event) {
+        if (event != PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN) {
+            mHandler.post(mUserActivityRunnable);
+        }
     }
 
     private void exitFaceDown(int resultType, long millisSinceFlip) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index f49e2f1..7555a7f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -549,6 +549,7 @@
             if (!mUserActivityPending) {
                 mUserActivityPending = true;
                 Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
+                msg.arg1 = event;
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
             }
@@ -647,7 +648,7 @@
         mSuspendBlocker.release();
     }
 
-    private void sendUserActivity() {
+    private void sendUserActivity(int event) {
         synchronized (mLock) {
             if (!mUserActivityPending) {
                 return;
@@ -657,7 +658,7 @@
         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
         tm.notifyUserActivity();
         mPolicy.userActivity();
-        mFaceDownDetector.userActivity();
+        mFaceDownDetector.userActivity(event);
     }
 
     void postEnhancedDischargePredictionBroadcast(long delayMs) {
@@ -833,7 +834,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_USER_ACTIVITY:
-                    sendUserActivity();
+                    sendUserActivity(msg.arg1);
                     break;
                 case MSG_BROADCAST:
                     sendNextBroadcast();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 29adde3..d2a4cd6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -180,8 +180,6 @@
     private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
     // Dirty bit: attentive timer may have timed out
     private static final int DIRTY_ATTENTIVE = 1 << 14;
-    // Dirty bit: phone flipped to face down
-    private static final int DIRTY_FACE_DOWN = 1 << 15;
     // Dirty bit: display group power state has changed
     private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16;
 
@@ -1069,8 +1067,9 @@
                 final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, -1L);
                 millisUntilNormalTimeout =
                         mLastUserActivityTime + screenOffTimeout - mClock.uptimeMillis();
-                mDirty |= DIRTY_FACE_DOWN;
-                updatePowerStateLocked();
+                userActivityInternal(mClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN, /* flags= */0,
+                        Process.SYSTEM_UID);
             }
         }
         if (isFaceDown) {
diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java
index 2e4b054..c9e81ed 100644
--- a/services/core/java/com/android/server/power/PreRebootLogger.java
+++ b/services/core/java/com/android/server/power/PreRebootLogger.java
@@ -19,7 +19,6 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.content.Context;
-import android.os.Binder;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -147,7 +146,6 @@
             return;
         }
 
-        final long token = Binder.clearCallingIdentity();
         try {
             final File dumpFile = new File(dumpDir, serviceName);
             final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
@@ -156,8 +154,6 @@
             binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
         } catch (FileNotFoundException | RemoteException e) {
             Slog.e(TAG, String.format("Failed to dump %s service before reboot", serviceName), e);
-        } finally {
-            Binder.restoreCallingIdentity(token);
         }
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 3a08ddc..fc62f5b 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -24,6 +24,7 @@
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
 import android.content.rollback.RollbackInfo;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -227,6 +228,15 @@
                 packageSessionIds, extensionVersions);
     }
 
+    private static boolean isLinkPossible(File oldFile, File newFile) {
+        try {
+            return Os.stat(oldFile.getAbsolutePath()).st_dev
+                    == Os.stat(newFile.getAbsolutePath()).st_dev;
+        } catch (ErrnoException ignore) {
+            return false;
+        }
+    }
+
     /**
      * Creates a backup copy of an apk or apex for a package.
      * For packages containing splits, this method should be called for each
@@ -239,16 +249,29 @@
         targetDir.mkdirs();
         File targetFile = new File(targetDir, sourceFile.getName());
 
-        try {
-            // Create a hard link to avoid copy
-            // TODO(b/168562373)
-            // Linking between non-encrypted and encrypted is not supported and we have
-            // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works
-            // because we happen to store encrypted files under /data/apex/active which is no
-            // longer the case when compressed apex rolls out. We have to handle this case in
-            // order not to fall back to copy.
-            Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
-        } catch (ErrnoException ignore) {
+        boolean fallbackToCopy = !isLinkPossible(sourceFile, targetFile);
+        if (!fallbackToCopy) {
+            try {
+                // Create a hard link to avoid copy
+                // TODO(b/168562373)
+                // Linking between non-encrypted and encrypted is not supported and we have
+                // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works
+                // because we happen to store encrypted files under /data/apex/active which is no
+                // longer the case when compressed apex rolls out. We have to handle this case in
+                // order not to fall back to copy.
+                Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
+            } catch (ErrnoException e) {
+                boolean isRollbackTest =
+                        SystemProperties.getBoolean("persist.rollback.is_test", false);
+                if (isRollbackTest) {
+                    throw new IOException(e);
+                } else {
+                    fallbackToCopy = true;
+                }
+            }
+        }
+
+        if (fallbackToCopy) {
             // Fall back to copy if hardlink can't be created
             Files.copy(sourceFile.toPath(), targetFile.toPath());
         }
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index cfb4c27..189f47f 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -77,9 +77,7 @@
 
     @GuardedBy("mLock")
     public void resolveRotationLocked(RotationRequest request) {
-        final RotationResolutionRequest remoteRequest = new RotationResolutionRequest(
-                request.mProposedRotation, request.mCurrentRotation, request.mPackageName,
-                request.mTimeoutMillis);
+        final RotationResolutionRequest remoteRequest = request.mRemoteRequest;
         post(service -> service.resolveRotation(request.mIRotationResolverCallback, remoteRequest));
 
         // schedule a timeout.
@@ -91,7 +89,7 @@
                     request.cancelInternal();
                 }
             }
-        }, request.mTimeoutMillis);
+        }, request.mRemoteRequest.getTimeoutMillis());
     }
 
     @VisibleForTesting
@@ -109,28 +107,18 @@
         @GuardedBy("mLock")
         boolean mIsFulfilled;
 
-        private final long mTimeoutMillis;
-
         @VisibleForTesting
-        final int mProposedRotation;
-
-        private final int mCurrentRotation;
-        private final String mPackageName;
+        final RotationResolutionRequest mRemoteRequest;
 
         boolean mIsDispatched;
         private final Object mLock = new Object();
         private final long mRequestStartTimeMillis;
 
         RotationRequest(
-                @NonNull RotationResolverInternal.RotationResolverCallbackInternal
-                        callbackInternal, int proposedRotation, int currentRotation,
-                String packageName, long timeoutMillis,
-                @NonNull CancellationSignal cancellationSignal) {
-            mTimeoutMillis = timeoutMillis;
+                @NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
+                RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal) {
             mCallbackInternal = callbackInternal;
-            mProposedRotation = proposedRotation;
-            mCurrentRotation = currentRotation;
-            mPackageName = packageName;
+            mRemoteRequest = request;
             mIRotationResolverCallback = new RotationResolverCallback(this);
             mCancellationSignalInternal = cancellationSignal;
             mRequestStartTimeMillis = SystemClock.elapsedRealtime();
@@ -185,8 +173,8 @@
                     request.mCallbackInternal.onSuccess(rotation);
                     final long timeToCalculate =
                             SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
-                    logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation,
-                            timeToCalculate);
+                    logRotationStats(request.mRemoteRequest.getProposedRotation(),
+                            request.mRemoteRequest.getCurrentRotation(), rotation, timeToCalculate);
                     Slog.d(TAG, "onSuccess:" + rotation);
                     Slog.d(TAG, "timeToCalculate:" + timeToCalculate);
                 }
@@ -204,8 +192,9 @@
                     request.mCallbackInternal.onFailure(error);
                     final long timeToCalculate =
                             SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
-                    logRotationStats(request.mProposedRotation, request.mCurrentRotation,
-                            RESOLUTION_FAILURE, timeToCalculate);
+                    logRotationStats(request.mRemoteRequest.getProposedRotation(),
+                            request.mRemoteRequest.getCurrentRotation(), RESOLUTION_FAILURE,
+                            timeToCalculate);
                     Slog.d(TAG, "onFailure:" + error);
                     Slog.d(TAG, "timeToCalculate:" + timeToCalculate);
                 }
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 13f8d61..1dbe3e4 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -34,11 +34,11 @@
 import android.content.pm.ServiceInfo;
 import android.os.CancellationSignal;
 import android.rotationresolver.RotationResolverInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
 import android.service.rotationresolver.RotationResolverService;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
-import android.view.Surface;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -95,14 +95,14 @@
     @VisibleForTesting
     void resolveRotationLocked(
             @NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
-            @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
-            String packageName, long timeoutMillis,
+            @NonNull RotationResolutionRequest request,
             @NonNull CancellationSignal cancellationSignalInternal) {
 
         if (!isServiceAvailableLocked()) {
             Slog.w(TAG, "Service is not available at this moment.");
             callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
-            logRotationStats(proposedRotation, currentRotation, RESOLUTION_UNAVAILABLE);
+            logRotationStats(request.getProposedRotation(), request.getCurrentRotation(),
+                    RESOLUTION_UNAVAILABLE);
             return;
         }
 
@@ -114,8 +114,7 @@
         }
 
         mCurrentRequest = new RemoteRotationResolverService.RotationRequest(callbackInternal,
-                proposedRotation, currentRotation, packageName, timeoutMillis,
-                cancellationSignalInternal);
+                request, cancellationSignalInternal);
 
         cancellationSignalInternal.setOnCancelListener(() -> {
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index e57d4ce..a7f3cdb 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -35,6 +35,7 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.rotationresolver.RotationResolverInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -158,8 +159,9 @@
                 if (mIsServiceEnabled) {
                     final RotationResolverManagerPerUserService service = getServiceForUserLocked(
                             UserHandle.getCallingUserId());
-                    service.resolveRotationLocked(callbackInternal, proposedRotation,
-                            currentRotation, /* packageName */ "", timeout,
+                    final RotationResolutionRequest request = new RotationResolutionRequest("",
+                            currentRotation, proposedRotation, true, timeout);
+                    service.resolveRotationLocked(callbackInternal, request,
                             cancellationSignalInternal);
                 } else {
                     Slog.w(TAG, "Rotation Resolver service is disabled.");
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index e5088c0..a0e04ee 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -22,6 +22,7 @@
 import android.os.CancellationSignal;
 import android.os.ShellCommand;
 import android.rotationresolver.RotationResolverInternal.RotationResolverCallbackInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
 import android.text.TextUtils;
 import android.view.Surface;
 
@@ -107,8 +108,10 @@
     }
 
     private int runResolveRotation() {
-        mService.resolveRotationLocked(sTestableRotationCallbackInternal, Surface.ROTATION_0,
-                Surface.ROTATION_0, "", 2000L, new CancellationSignal());
+        final RotationResolutionRequest request = new RotationResolutionRequest("",
+                Surface.ROTATION_0, Surface.ROTATION_0,  true, 2000L);
+        mService.resolveRotationLocked(sTestableRotationCallbackInternal, request,
+                new CancellationSignal());
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
index dbe7354..52c1467b 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.speech;
 
+import static android.Manifest.permission.MANAGE_SPEECH_RECOGNITION;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
@@ -24,6 +26,7 @@
 import android.os.UserHandle;
 import android.speech.IRecognitionServiceManager;
 import android.speech.IRecognitionServiceManagerCallback;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.server.infra.AbstractMasterSystemService;
@@ -42,6 +45,8 @@
                 SpeechRecognitionManagerServiceImpl> {
     private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName();
 
+    private static final int MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS = 60_000;
+
     public SpeechRecognitionManagerService(@NonNull Context context) {
         super(context,
                 // TODO(b/176578753): think if we want to favor the particular service here.
@@ -58,6 +63,16 @@
     }
 
     @Override
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_SPEECH_RECOGNITION, TAG);
+    }
+
+    @Override
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS;
+    }
+
+    @Override
     protected SpeechRecognitionManagerServiceImpl newServiceLocked(
             @UserIdInt int resolvedUserId, boolean disabled) {
         return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
@@ -77,5 +92,21 @@
                 service.createSessionLocked(componentName, clientToken, onDevice, callback);
             }
         }
+
+        @Override
+        public void setTemporaryComponent(ComponentName componentName) {
+            int userId = UserHandle.getCallingUserId();
+            if (componentName == null) {
+                resetTemporaryService(userId);
+                Slog.i(TAG, "Reset temporary service for user " + userId);
+                return;
+            }
+            setTemporaryService(
+                    userId,
+                    componentName.flattenToString(),
+                    MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS);
+            Slog.i(TAG, "SpeechRecognition temporarily set to " + componentName + " for "
+                    + MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS + "ms");
+        }
     }
 }
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 2656a3d..769e049 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -100,6 +100,9 @@
         }
 
         if (serviceComponent == null) {
+            if (mMaster.debug) {
+                Slog.i(TAG, "Service component is undefined, responding with error.");
+            }
             tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT);
             return;
         }
@@ -213,6 +216,10 @@
     @Nullable
     private ComponentName getOnDeviceComponentNameLocked() {
         final String serviceName = getComponentNameLocked();
+        if (mMaster.debug) {
+            Slog.i(TAG, "Resolved component name: " + serviceName);
+        }
+
         if (serviceName == null) {
             if (mMaster.verbose) {
                 Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
@@ -241,6 +248,11 @@
                                         service.getServiceComponentName().equals(serviceComponent))
                                 .findFirst();
                 if (existingService.isPresent()) {
+
+                    if (mMaster.debug) {
+                        Slog.i(TAG, "Reused existing connection to " + serviceComponent);
+                    }
+
                     return existingService.get();
                 }
             }
@@ -253,6 +265,10 @@
                     mRemoteServicesByUid.computeIfAbsent(callingUid, key -> new HashSet<>());
             valuesByCaller.add(service);
 
+            if (mMaster.debug) {
+                Slog.i(TAG, "Creating a new connection to " + serviceComponent);
+            }
+
             return service;
         }
     }
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index fc7fd22..174ad3a 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,7 +1,10 @@
 jeffreyhuang@google.com
 joeo@google.com
+jtnguyen@google.com
 muhammadq@google.com
+rslawik@google.com
 ruchirr@google.com
+sharaienko@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
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 7ed7a59..8023fd4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -148,11 +148,13 @@
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+import com.android.internal.os.KernelSingleProcessCpuThreadReader.ProcessCpuUsage;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
 import com.android.internal.os.LooperStats;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.SelectedProcessCpuThreadReader;
 import com.android.internal.os.StoragedUidIoStatsReader;
 import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
 import com.android.internal.util.CollectionUtils;
@@ -351,6 +353,8 @@
     @GuardedBy("mDataBytesTransferLock")
     private final ArrayList<SubInfo> mHistoricalSubs = new ArrayList<>();
 
+    private SelectedProcessCpuThreadReader mSurfaceFlingerProcessCpuThreadReader;
+
     // Puller locks
     private final Object mDataBytesTransferLock = new Object();
     private final Object mBluetoothBytesTransferLock = new Object();
@@ -753,6 +757,9 @@
                 }
             }
         }
+
+        mSurfaceFlingerProcessCpuThreadReader =
+                new SelectedProcessCpuThreadReader("/system/bin/surfaceflinger");
     }
 
     void registerEventListeners() {
@@ -1479,7 +1486,7 @@
         }
         for (int freqIndex = 0; freqIndex < timesMs.length; ++freqIndex) {
             int cluster = freqsClusters[freqIndex];
-            long freq = freqs[freqIndex];
+            int freq = (int) freqs[freqIndex];
             long timeMs = timesMs[freqIndex];
             pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs));
         }
@@ -1678,6 +1685,18 @@
                 FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER,
                 times.binderThreadCpuTimesUs);
 
+        ProcessCpuUsage surfaceFlingerTimes = mSurfaceFlingerProcessCpuThreadReader.readAbsolute();
+        if (surfaceFlingerTimes != null && surfaceFlingerTimes.threadCpuTimesMillis != null) {
+            long[] surfaceFlingerTimesUs =
+                    new long[surfaceFlingerTimes.threadCpuTimesMillis.length];
+            for (int i = 0; i < surfaceFlingerTimesUs.length; ++i) {
+                surfaceFlingerTimesUs[i] = surfaceFlingerTimes.threadCpuTimesMillis[i] * 1_000;
+            }
+            addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
+                    FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SURFACE_FLINGER,
+                    surfaceFlingerTimesUs);
+        }
+
         return StatsManager.PULL_SUCCESS;
     }
 
diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
index 628c1d6..9f8b27f 100644
--- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
@@ -26,7 +26,7 @@
     private SystemMemoryUtil() {}
 
     static Metrics getMetrics() {
-        int totalIonKb = (int) Debug.getIonHeapsSizeKb();
+        int totalIonKb = (int) Debug.getDmabufHeapTotalExportedKb();
         int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb();
         int gpuDmaBufUsageKb = (int) Debug.getGpuDmaBufUsageKb();
         int dmaBufTotalExportedKb = (int) Debug.getDmabufTotalExportedKb();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 7523671..970420a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -160,4 +160,10 @@
      * Handles a logging command from the WM shell command.
      */
     void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd);
+
+    /**
+     * @see com.android.internal.statusbar.IStatusBar#setNavigationBarLumaSamplingEnabled(int,
+     * boolean)
+     */
+    void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 546e420..302a23f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -588,6 +588,15 @@
                 } catch (RemoteException ex) { }
             }
         }
+
+        @Override
+        public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+            if (mBar != null) {
+                try {
+                    mBar.setNavigationBarLumaSamplingEnabled(displayId, enable);
+                } catch (RemoteException ex) { }
+            }
+        }
     };
 
     private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 3ae9d64..ee78a4e 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -28,8 +28,6 @@
 import android.app.time.TimeZoneConfiguration;
 import android.os.UserHandle;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Objects;
 
 /**
@@ -39,7 +37,7 @@
  */
 public final class ConfigurationInternal {
 
-    private final boolean mAutoDetectionSupported;
+    private final boolean mTelephonyDetectionSupported;
     private final boolean mGeoDetectionSupported;
     private final boolean mAutoDetectionEnabled;
     private final @UserIdInt int mUserId;
@@ -48,7 +46,7 @@
     private final boolean mGeoDetectionEnabled;
 
     private ConfigurationInternal(Builder builder) {
-        mAutoDetectionSupported = builder.mAutoDetectionSupported;
+        mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
         mGeoDetectionSupported = builder.mGeoDetectionSupported;
         mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
 
@@ -56,14 +54,16 @@
         mUserConfigAllowed = builder.mUserConfigAllowed;
         mLocationEnabled = builder.mLocationEnabled;
         mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
-        // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
-        // cannot be true if mAutoDetectionSupported == false
-        Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
     }
 
     /** Returns true if the device supports any form of auto time zone detection. */
     public boolean isAutoDetectionSupported() {
-        return mAutoDetectionSupported;
+        return mTelephonyDetectionSupported || mGeoDetectionSupported;
+    }
+
+    /** Returns true if the device supports telephony time zone detection. */
+    public boolean isTelephonyDetectionSupported() {
+        return mTelephonyDetectionSupported;
     }
 
     /** Returns true if the device supports geolocation time zone detection. */
@@ -78,9 +78,10 @@
 
     /**
      * Returns true if auto time zone detection behavior is actually enabled, which can be distinct
-     * from the raw setting value. */
+     * from the raw setting value.
+     */
     public boolean getAutoDetectionEnabledBehavior() {
-        return mAutoDetectionSupported && mAutoDetectionEnabled;
+        return isAutoDetectionSupported() && mAutoDetectionEnabled;
     }
 
     /** Returns the ID of the user this configuration is associated with. */
@@ -212,7 +213,7 @@
         ConfigurationInternal that = (ConfigurationInternal) o;
         return mUserId == that.mUserId
                 && mUserConfigAllowed == that.mUserConfigAllowed
-                && mAutoDetectionSupported == that.mAutoDetectionSupported
+                && mTelephonyDetectionSupported == that.mTelephonyDetectionSupported
                 && mGeoDetectionSupported == that.mGeoDetectionSupported
                 && mAutoDetectionEnabled == that.mAutoDetectionEnabled
                 && mLocationEnabled == that.mLocationEnabled
@@ -221,7 +222,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
+        return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
                 mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled,
                 mGeoDetectionEnabled);
     }
@@ -231,7 +232,7 @@
         return "ConfigurationInternal{"
                 + "mUserId=" + mUserId
                 + ", mUserConfigAllowed=" + mUserConfigAllowed
-                + ", mAutoDetectionSupported=" + mAutoDetectionSupported
+                + ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
                 + ", mGeoDetectionSupported=" + mGeoDetectionSupported
                 + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
                 + ", mLocationEnabled=" + mLocationEnabled
@@ -247,7 +248,7 @@
         private final @UserIdInt int mUserId;
 
         private boolean mUserConfigAllowed;
-        private boolean mAutoDetectionSupported;
+        private boolean mTelephonyDetectionSupported;
         private boolean mGeoDetectionSupported;
         private boolean mAutoDetectionEnabled;
         private boolean mLocationEnabled;
@@ -266,7 +267,7 @@
         public Builder(ConfigurationInternal toCopy) {
             this.mUserId = toCopy.mUserId;
             this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
-            this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+            this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported;
             this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
             this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
             this.mLocationEnabled = toCopy.mLocationEnabled;
@@ -282,10 +283,10 @@
         }
 
         /**
-         * Sets whether any form of automatic time zone detection is supported on this device.
+         * Sets whether telephony time zone detection is supported on this device.
          */
-        public Builder setAutoDetectionFeatureSupported(boolean supported) {
-            mAutoDetectionSupported = supported;
+        public Builder setTelephonyDetectionFeatureSupported(boolean supported) {
+            mTelephonyDetectionSupported = supported;
             return this;
         }
 
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index e3caae94..0e5f3bf 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -128,8 +128,8 @@
     @Override
     public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
         return new ConfigurationInternal.Builder(userId)
-                .setAutoDetectionFeatureSupported(
-                        mServiceConfigAccessor.isAutoDetectionFeatureSupported())
+                .setTelephonyDetectionFeatureSupported(
+                        mServiceConfigAccessor.isTelephonyTimeZoneDetectionFeatureSupported())
                 .setGeoDetectionFeatureSupported(
                         mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported())
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
new file mode 100644
index 0000000..c8c828f
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -0,0 +1,344 @@
+/*
+ * 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.timezonedetector;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that provides time zone detector state information for metrics.
+ *
+ * <p>
+ * Regarding time zone ID ordinals:
+ * <p>
+ * We don't want to leak user location information by reporting time zone IDs. Instead, time zone
+ * IDs are consistently identified within a given instance of this class by a numeric ID. This
+ * allows comparison of IDs without revealing what those IDs are.
+ */
+public final class MetricsTimeZoneDetectorState {
+
+    @IntDef(prefix = "DETECTION_MODE_",
+            value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY})
+    @interface DetectionMode {};
+
+    @DetectionMode
+    public static final int DETECTION_MODE_MANUAL = 0;
+    @DetectionMode
+    public static final int DETECTION_MODE_GEO = 1;
+    @DetectionMode
+    public static final int DETECTION_MODE_TELEPHONY = 2;
+
+    @NonNull
+    private final ConfigurationInternal mConfigurationInternal;
+    @NonNull
+    private final int mDeviceTimeZoneIdOrdinal;
+    @Nullable
+    private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
+    @Nullable
+    private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
+    @Nullable
+    private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
+
+    private MetricsTimeZoneDetectorState(
+            @NonNull ConfigurationInternal configurationInternal,
+            int deviceTimeZoneIdOrdinal,
+            @Nullable MetricsTimeZoneSuggestion latestManualSuggestion,
+            @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion,
+            @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) {
+        mConfigurationInternal = Objects.requireNonNull(configurationInternal);
+        mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal;
+        mLatestManualSuggestion = latestManualSuggestion;
+        mLatestTelephonySuggestion = latestTelephonySuggestion;
+        mLatestGeolocationSuggestion = latestGeolocationSuggestion;
+    }
+
+    /**
+     * Creates {@link MetricsTimeZoneDetectorState} from the supplied parameters, using the {@link
+     * OrdinalGenerator} to generate time zone ID ordinals.
+     */
+    public static MetricsTimeZoneDetectorState create(
+            @NonNull OrdinalGenerator<String> tzIdOrdinalGenerator,
+            @NonNull ConfigurationInternal configurationInternal,
+            @NonNull String deviceTimeZoneId,
+            @Nullable ManualTimeZoneSuggestion latestManualSuggestion,
+            @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
+            @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
+
+        // TODO(b/172934905) Add logic to canonicalize the time zone IDs to Android's preferred IDs
+        //  so that the ordinals will match even when the ID is not identical, just equivalent.
+        int deviceTimeZoneIdOrdinal =
+                tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
+        MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion =
+                createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion);
+        MetricsTimeZoneSuggestion latestObfuscatedTelephonySuggestion =
+                createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion);
+        MetricsTimeZoneSuggestion latestObfuscatedGeolocationSuggestion =
+                createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion);
+
+        return new MetricsTimeZoneDetectorState(
+                configurationInternal, deviceTimeZoneIdOrdinal, latestObfuscatedManualSuggestion,
+                latestObfuscatedTelephonySuggestion, latestObfuscatedGeolocationSuggestion);
+    }
+
+    /** Returns true if the device supports telephony time zone detection. */
+    public boolean isTelephonyDetectionSupported() {
+        return mConfigurationInternal.isTelephonyDetectionSupported();
+    }
+
+    /** Returns true if the device supports geolocation time zone detection. */
+    public boolean isGeoDetectionSupported() {
+        return mConfigurationInternal.isGeoDetectionSupported();
+    }
+
+    /** Returns true if user's location can be used generally. */
+    public boolean isUserLocationEnabled() {
+        return mConfigurationInternal.isLocationEnabled();
+    }
+
+    /** Returns the value of the geolocation time zone detection enabled setting. */
+    public boolean getGeoDetectionEnabledSetting() {
+        return mConfigurationInternal.getGeoDetectionEnabledSetting();
+    }
+
+    /** Returns the value of the auto time zone detection enabled setting. */
+    public boolean getAutoDetectionEnabledSetting() {
+        return mConfigurationInternal.getAutoDetectionEnabledSetting();
+    }
+
+    /**
+     * Returns the detection mode the device is currently using, which can be influenced by various
+     * things besides the user's setting.
+     */
+    @DetectionMode
+    public int getDetectionMode() {
+        if (!mConfigurationInternal.getAutoDetectionEnabledBehavior()) {
+            return DETECTION_MODE_MANUAL;
+        } else if (mConfigurationInternal.getGeoDetectionEnabledBehavior()) {
+            return DETECTION_MODE_GEO;
+        } else {
+            return DETECTION_MODE_TELEPHONY;
+        }
+    }
+
+    /**
+     * Returns the ordinal for the device's currently set time zone ID.
+     * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
+     */
+    @NonNull
+    public int getDeviceTimeZoneIdOrdinal() {
+        return mDeviceTimeZoneIdOrdinal;
+    }
+
+    /**
+     * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last manual
+     * suggestion received.
+     */
+    @Nullable
+    public byte[] getLatestManualSuggestionProtoBytes() {
+        return suggestionProtoBytes(mLatestManualSuggestion);
+    }
+
+    /**
+     * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last, best
+     * telephony suggestion received.
+     */
+    @Nullable
+    public byte[] getLatestTelephonySuggestionProtoBytes() {
+        return suggestionProtoBytes(mLatestTelephonySuggestion);
+    }
+
+    /**
+     * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last geolocation
+     * suggestion received.
+     */
+    @Nullable
+    public byte[] getLatestGeolocationSuggestionProtoBytes() {
+        return suggestionProtoBytes(mLatestGeolocationSuggestion);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o;
+        return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal
+                && mConfigurationInternal.equals(that.mConfigurationInternal)
+                && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion)
+                && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion)
+                && Objects.equals(mLatestGeolocationSuggestion, that.mLatestGeolocationSuggestion);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal,
+                mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
+    }
+
+    @Override
+    public String toString() {
+        return "MetricsTimeZoneDetectorState{"
+                + "mConfigurationInternal=" + mConfigurationInternal
+                + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+                + ", mLatestManualSuggestion=" + mLatestManualSuggestion
+                + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
+                + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
+                + '}';
+    }
+
+    private static byte[] suggestionProtoBytes(
+            @Nullable MetricsTimeZoneSuggestion suggestion) {
+        if (suggestion == null) {
+            return null;
+        }
+        return suggestion.toBytes();
+    }
+
+    @Nullable
+    private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+            @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+            @NonNull ManualTimeZoneSuggestion manualSuggestion) {
+        if (manualSuggestion == null) {
+            return null;
+        }
+
+        int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId());
+        return MetricsTimeZoneSuggestion.createCertain(
+                new int[] { zoneIdOrdinal });
+    }
+
+    @Nullable
+    private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+            @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+            @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) {
+        if (telephonySuggestion == null) {
+            return null;
+        }
+        if (telephonySuggestion.getZoneId() == null) {
+            return MetricsTimeZoneSuggestion.createUncertain();
+        }
+        int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId());
+        return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal });
+    }
+
+    @Nullable
+    private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+            @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+            @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) {
+        if (geolocationSuggestion == null) {
+            return null;
+        }
+
+        List<String> zoneIds = geolocationSuggestion.getZoneIds();
+        if (zoneIds == null) {
+            return MetricsTimeZoneSuggestion.createUncertain();
+        }
+        return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds));
+    }
+
+    /**
+     * A Java class that closely matches the android.app.time.MetricsTimeZoneSuggestion
+     * proto definition.
+     */
+    private static final class MetricsTimeZoneSuggestion {
+        @Nullable
+        private final int[] mZoneIdOrdinals;
+
+        MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) {
+            mZoneIdOrdinals = zoneIdOrdinals;
+        }
+
+        @NonNull
+        static MetricsTimeZoneSuggestion createUncertain() {
+            return new MetricsTimeZoneSuggestion(null);
+        }
+
+        public static MetricsTimeZoneSuggestion createCertain(
+                @NonNull int[] zoneIdOrdinals) {
+            return new MetricsTimeZoneSuggestion(zoneIdOrdinals);
+        }
+
+        boolean isCertain() {
+            return mZoneIdOrdinals != null;
+        }
+
+        @Nullable
+        int[] getZoneIdOrdinals() {
+            return mZoneIdOrdinals;
+        }
+
+        byte[] toBytes() {
+            // We don't get access to the atoms.proto definition for nested proto fields, so we use
+            // an identically specified proto.
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream);
+            int typeProtoValue = isCertain()
+                    ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN
+                    : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN;
+            protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE,
+                    typeProtoValue);
+            if (isCertain()) {
+                for (int zoneIdOrdinal : getZoneIdOrdinals()) {
+                    protoOutputStream.write(
+                            android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
+                            zoneIdOrdinal);
+                }
+            }
+            protoOutputStream.flush();
+            closeQuietly(byteArrayOutputStream);
+            return byteArrayOutputStream.toByteArray();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o;
+            return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals);
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(mZoneIdOrdinals);
+        }
+
+        @Override
+        public String toString() {
+            return "MetricsTimeZoneSuggestion{"
+                    + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals)
+                    + '}';
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
new file mode 100644
index 0000000..a448773
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.timezonedetector;
+
+import android.util.ArraySet;
+
+import java.util.List;
+
+/**
+ * A helper class that turns a set of objects into ordinal values, i.e. each object is offered
+ * up via {@link #ordinal(Object)} or similar method, and a number will be returned. If the
+ * object has been seen before by the instance then the same number will be returned. Intended
+ * for situations where it is useful to know if values from some finite set are the same or
+ * different, but the value is either large or may reveal PII. This class relies on {@link
+ * Object#equals(Object)} and {@link Object#hashCode()}.
+ */
+class OrdinalGenerator<T> {
+    private final ArraySet<T> mKnownIds = new ArraySet<>();
+
+    int ordinal(T object) {
+        int ordinal = mKnownIds.indexOf(object);
+        if (ordinal < 0) {
+            ordinal = mKnownIds.size();
+            mKnownIds.add(object);
+        }
+        return ordinal;
+    }
+
+    int[] ordinals(List<T> objects) {
+        int[] ordinals = new int[objects.size()];
+        for (int i = 0; i < ordinals.length; i++) {
+            ordinals[i] = ordinal(objects.get(i));
+        }
+        return ordinals;
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 86c32f8..2452c8d 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -115,10 +115,15 @@
 
     /** Returns {@code true} if any form of automatic time zone detection is supported. */
     public boolean isAutoDetectionFeatureSupported() {
-        return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported();
+        return isTelephonyTimeZoneDetectionFeatureSupported()
+                || isGeoTimeZoneDetectionFeatureSupported();
     }
 
-    private boolean deviceHasTelephonyNetwork() {
+    /**
+     * Returns {@code true} if the telephony-based time zone detection feature is supported on the
+     * device.
+     */
+    public boolean isTelephonyTimeZoneDetectionFeatureSupported() {
         // TODO b/150583524 Avoid the use of a deprecated API.
         return mContext.getSystemService(ConnectivityManager.class)
                 .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index cd220b1..d429b87 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -50,4 +50,8 @@
      * available, and so on. This method may be implemented asynchronously.
      */
     void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
+
+    /** Generates a state snapshot for metrics. */
+    @NonNull
+    MetricsTimeZoneDetectorState generateMetricsState();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 2d5dacd..4e78f5a 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -86,8 +86,14 @@
             @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
         Objects.requireNonNull(timeZoneSuggestion);
 
-        // All strategy calls must take place on the mHandler thread.
+        // This call can take place on the mHandler thread because there is no return value.
         mHandler.post(
                 () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
     }
+
+    @Override
+    @NonNull
+    public MetricsTimeZoneDetectorState generateMetricsState() {
+        return mTimeZoneDetectorStrategy.generateMetricsState();
+    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 8266f12..e3f31b6 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -66,9 +66,10 @@
  * <p>Threading:
  *
  * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
- * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug
- * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread
- * concurrently with other operations.
+ * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()},
+ * {@link #generateMetricsState()} and debug calls like {@link
+ * #dump(IndentingPrintWriter, String[])}, may be called on a different thread concurrently with
+ * other operations.
  *
  * @hide
  */
@@ -123,4 +124,8 @@
      * suggestion.
      */
     void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
+
+    /** Generates a state snapshot for metrics. */
+    @NonNull
+    MetricsTimeZoneDetectorState generateMetricsState();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index d163a0e..5d34dd7 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -195,6 +195,13 @@
     private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
             new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
+    /**
+     * The latest manual suggestion received.
+     */
+    @GuardedBy("this")
+    private ReferenceWithHistory<ManualTimeZoneSuggestion> mLatestManualSuggestion =
+            new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
     @GuardedBy("this")
     private final List<Dumpable> mDumpables = new ArrayList<>();
 
@@ -286,6 +293,7 @@
 
         if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
             // Only store a geolocation suggestion if geolocation detection is currently enabled.
+            // See also clearGeolocationSuggestionIfNeeded().
             mLatestGeoLocationSuggestion.set(suggestion);
 
             // Now perform auto time zone detection. The new suggestion may be used to modify the
@@ -324,6 +332,12 @@
             return false;
         }
 
+        // Record the manual suggestion for debugging / metrics (but only if manual detection is
+        // currently enabled).
+        // Note: This is not used to set the device back to a previous manual suggestion if the user
+        // later disables automatic time zone detection.
+        mLatestManualSuggestion.set(suggestion);
+
         setDeviceTimeZoneIfRequired(timeZoneId, cause);
         return true;
     }
@@ -357,6 +371,28 @@
         }
     }
 
+    @Override
+    @NonNull
+    public synchronized MetricsTimeZoneDetectorState generateMetricsState() {
+        int currentUserId = mEnvironment.getCurrentUserId();
+        // Just capture one telephony suggestion: the one that would be used right now if telephony
+        // detection is in use.
+        QualifiedTelephonyTimeZoneSuggestion bestQualifiedTelephonySuggestion =
+                findBestTelephonySuggestion();
+        TelephonyTimeZoneSuggestion telephonySuggestion =
+                bestQualifiedTelephonySuggestion == null
+                        ? null : bestQualifiedTelephonySuggestion.suggestion;
+        // A new generator is created each time: we don't want / require consistency.
+        OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+        return MetricsTimeZoneDetectorState.create(
+                tzIdOrdinalGenerator,
+                getConfigurationInternal(currentUserId),
+                mEnvironment.getDeviceTimeZone(),
+                getLatestManualSuggestion(),
+                telephonySuggestion,
+                getLatestGeolocationSuggestion());
+    }
+
     private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
         int score;
         if (suggestion.getZoneId() == null) {
@@ -619,6 +655,11 @@
         mTimeZoneChangesLog.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
+        ipw.println("Manual suggestion history:");
+        ipw.increaseIndent(); // level 2
+        mLatestManualSuggestion.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
         ipw.println("Geolocation suggestion history:");
         ipw.increaseIndent(); // level 2
         mLatestGeoLocationSuggestion.dump(ipw);
@@ -639,6 +680,14 @@
      * A method used to inspect strategy state during tests. Not intended for general use.
      */
     @VisibleForTesting
+    public synchronized ManualTimeZoneSuggestion getLatestManualSuggestion() {
+        return mLatestManualSuggestion.get();
+    }
+
+    /**
+     * A method used to inspect strategy state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
     public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion(
             int slotIndex) {
         return mTelephonySuggestionsBySlotIndex.get(slotIndex);
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index c0c9e6d..4fa920e 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -39,15 +39,15 @@
  */
 class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
 
-    private static final String TAG = LocationTimeZoneManagerService.TAG;
-
     @NonNull private final LocationTimeZoneProviderProxy mProxy;
 
     BinderLocationTimeZoneProvider(
+            @NonNull ProviderMetricsLogger providerMetricsLogger,
             @NonNull ThreadingDomain threadingDomain,
             @NonNull String providerName,
             @NonNull LocationTimeZoneProviderProxy proxy) {
-        super(threadingDomain, providerName);
+        super(providerMetricsLogger, threadingDomain, providerName,
+                new ZoneInfoDbTimeZoneIdValidator());
         mProxy = Objects.requireNonNull(proxy);
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 0d1692a..ca4a640 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -45,6 +45,7 @@
 import com.android.server.SystemService;
 import com.android.server.timezonedetector.ServiceConfigAccessor;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -297,7 +298,9 @@
                     R.string.config_primaryLocationTimeZoneProviderPackageName
             );
         }
-        return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
+        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
+        return new BinderLocationTimeZoneProvider(
+                providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
     }
 
     @NonNull
@@ -317,7 +320,9 @@
                     R.string.config_secondaryLocationTimeZoneProviderPackageName
             );
         }
-        return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
+        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
+        return new BinderLocationTimeZoneProvider(
+                providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
     }
 
     /** Used for bug triage and in tests to simulate provider events. */
@@ -520,6 +525,12 @@
         }
     }
 
+    static void infoLog(String msg) {
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Slog.i(TAG, msg);
+        }
+    }
+
     static void warnLog(String msg) {
         warnLog(msg, null);
     }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index ef2f357..cc815dc6 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -20,6 +20,7 @@
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
 
 import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog;
 import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
@@ -85,6 +86,26 @@
     }
 
     /**
+     * Used by {@link LocationTimeZoneProvider} to check if time zone IDs are understood
+     * by the platform.
+     */
+    interface TimeZoneIdValidator {
+
+        /**
+         * Returns whether {@code timeZoneId} is supported by the platform or not.
+         */
+        boolean isValid(@NonNull String timeZoneId);
+    }
+
+    /**
+     * Listener interface used to log provider events for metrics.
+     */
+    interface ProviderMetricsLogger {
+        /** Logs that a provider changed state. */
+        void onProviderStateChanged(@ProviderStateEnum int stateEnum);
+    }
+
+    /**
      * Information about the provider's current state.
      */
     static class ProviderState {
@@ -336,6 +357,7 @@
         }
     }
 
+    @NonNull private final ProviderMetricsLogger mProviderMetricsLogger;
     @NonNull final ThreadingDomain mThreadingDomain;
     @NonNull final Object mSharedLock;
     @NonNull final String mProviderName;
@@ -364,13 +386,19 @@
     // Non-null and effectively final after initialize() is called.
     ProviderListener mProviderListener;
 
+    @NonNull private TimeZoneIdValidator mTimeZoneIdValidator;
+
     /** Creates the instance. */
-    LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
-            @NonNull String providerName) {
+    LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+            @NonNull ThreadingDomain threadingDomain,
+            @NonNull String providerName,
+            @NonNull TimeZoneIdValidator timeZoneIdValidator) {
         mThreadingDomain = Objects.requireNonNull(threadingDomain);
+        mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
         mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
         mSharedLock = threadingDomain.getLockObject();
         mProviderName = Objects.requireNonNull(providerName);
+        mTimeZoneIdValidator = Objects.requireNonNull(timeZoneIdValidator);
     }
 
     /**
@@ -468,6 +496,7 @@
             mCurrentState.set(newState);
             onSetCurrentState(newState);
             if (!Objects.equals(newState, oldState)) {
+                mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum);
                 if (mStateChangeRecording) {
                     mRecordedStates.add(newState);
                 }
@@ -610,6 +639,25 @@
         mThreadingDomain.assertCurrentThread();
         Objects.requireNonNull(timeZoneProviderEvent);
 
+        // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set
+        // the device's time zone. This logic prevents bad time zone IDs entering the time zone
+        // detection logic from third party code.
+        //
+        // An event containing an unknown time zone ID could occur if the provider is using a
+        // different TZDB version than the device. Provider developers are expected to take steps to
+        // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone
+        // rules, or providing IDs based on the device's TZDB version, so this is not considered a
+        // common case.
+        //
+        // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary
+        // enables immediate failover to a secondary provider, one that might provide valid IDs for
+        // the same location, which should provide better behavior than just ignoring the event.
+        if (hasInvalidTimeZones(timeZoneProviderEvent)) {
+            infoLog("event=" + timeZoneProviderEvent + " has unsupported time zones. "
+                    + "Replacing it with uncertain event.");
+            timeZoneProviderEvent = TimeZoneProviderEvent.createUncertainEvent();
+        }
+
         synchronized (mSharedLock) {
             debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName
                     + ", timeZoneProviderEvent=" + timeZoneProviderEvent);
@@ -707,6 +755,20 @@
         }
     }
 
+    private boolean hasInvalidTimeZones(@NonNull TimeZoneProviderEvent event) {
+        if (event.getSuggestion() == null) {
+            return false;
+        }
+
+        for (String timeZone : event.getSuggestion().getTimeZoneIds()) {
+            if (!mTimeZoneIdValidator.isValid(timeZone)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     @GuardedBy("mSharedLock")
     private void assertIsStarted() {
         ProviderState currentState = mCurrentState.get();
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
new file mode 100644
index 0000000..dfff6f2
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.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.server.timezonedetector.location;
+
+import android.annotation.IntRange;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+
+/**
+ * The real implementation of {@link ProviderMetricsLogger} which logs using
+ * {@link FrameworkStatsLog}.
+ */
+public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+
+    @IntRange(from = 0, to = 1)
+    private final int mProviderIndex;
+
+    public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+        mProviderIndex = providerIndex;
+    }
+
+    @Override
+    public void onProviderStateChanged(@ProviderStateEnum int stateEnum) {
+        // TODO(b/172934905): Implement once the atom has landed.
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
new file mode 100644
index 0000000..cab5ad2
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
@@ -0,0 +1,30 @@
+/*
+ * 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.timezonedetector.location;
+
+import android.annotation.NonNull;
+
+import com.android.i18n.timezone.ZoneInfoDb;
+
+class ZoneInfoDbTimeZoneIdValidator implements
+        LocationTimeZoneProvider.TimeZoneIdValidator {
+
+    @Override
+    public boolean isValid(@NonNull String timeZoneId) {
+        return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId);
+    }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a1d2f8a..3ae67ae 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -3298,7 +3298,11 @@
                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
                             sessionToken.toString());
 
-                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    try{
+                        mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    }catch(IllegalArgumentException ex){
+                        Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex);
+                    }
                     args.recycle();
                     break;
                 }
@@ -3313,7 +3317,11 @@
                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
                             sessionToken.toString());
 
-                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    try{
+                        mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    }catch(IllegalArgumentException ex){
+                        Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex);
+                    }
                     args.recycle();
                     break;
                 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 3893267..b90408f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.vibrator.IVibratorManager;
 import android.os.CombinedVibrationEffect;
@@ -35,9 +36,9 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.FrameworkStatsLog;
 
-import com.google.android.collect.Lists;
-
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.PriorityQueue;
 
@@ -47,11 +48,16 @@
     private static final boolean DEBUG = false;
 
     /**
-     * Extra timeout added to the end of each synced vibration step as a timeout for the callback
-     * wait, to ensure it finishes even when callbacks from individual vibrators are lost.
+     * Extra timeout added to the end of each vibration step to ensure it finishes even when
+     * vibrator callbacks are lost.
      */
     private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
 
+    /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
+    private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
+
+    private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
+
     /** Callbacks for playing a {@link Vibration}. */
     interface VibrationCallbacks {
 
@@ -83,13 +89,10 @@
     private final IBatteryStats mBatteryStatsService;
     private final Vibration mVibration;
     private final VibrationCallbacks mCallbacks;
-    private final SparseArray<VibratorController> mVibrators;
+    private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
+    private final StepQueue mStepQueue = new StepQueue();
 
-    @GuardedBy("mLock")
-    @Nullable
-    private VibrateStep mCurrentVibrateStep;
-    @GuardedBy("mLock")
-    private boolean mForceStop;
+    private volatile boolean mForceStop;
 
     VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
             PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
@@ -102,7 +105,6 @@
         mBatteryStatsService = batteryStatsService;
 
         CombinedVibrationEffect effect = vib.getEffect();
-        mVibrators = new SparseArray<>();
         for (int i = 0; i < availableVibrators.size(); i++) {
             if (effect.hasVibrator(availableVibrators.keyAt(i))) {
                 mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
@@ -110,6 +112,15 @@
         }
     }
 
+    Vibration getVibration() {
+        return mVibration;
+    }
+
+    @VisibleForTesting
+    SparseArray<VibratorController> getVibrators() {
+        return mVibrators;
+    }
+
     @Override
     public void binderDied() {
         if (DEBUG) {
@@ -136,8 +147,11 @@
 
     /** Cancel current vibration and shuts down the thread gracefully. */
     public void cancel() {
+        mForceStop = true;
         synchronized (mLock) {
-            mForceStop = true;
+            if (DEBUG) {
+                Slog.d(TAG, "Vibration cancelled");
+            }
             mLock.notify();
         }
     }
@@ -148,11 +162,10 @@
             if (DEBUG) {
                 Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
             }
-            if (mCurrentVibrateStep != null) {
-                for (int i = 0; i < mVibrators.size(); i++) {
-                    mCurrentVibrateStep.vibratorComplete(mVibrators.keyAt(i));
-                }
+            for (int i = 0; i < mVibrators.size(); i++) {
+                mStepQueue.consumeOnVibratorComplete(mVibrators.keyAt(i));
             }
+            mLock.notify();
         }
     }
 
@@ -162,120 +175,73 @@
             if (DEBUG) {
                 Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
             }
-            if (mCurrentVibrateStep != null) {
-                mCurrentVibrateStep.vibratorComplete(vibratorId);
-            }
+            mStepQueue.consumeOnVibratorComplete(vibratorId);
+            mLock.notify();
         }
     }
 
-    Vibration getVibration() {
-        return mVibration;
-    }
-
-    @VisibleForTesting
-    SparseArray<VibratorController> getVibrators() {
-        return mVibrators;
-    }
-
     private Vibration.Status playVibration() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
         try {
-            List<Step> steps = generateSteps(mVibration.getEffect());
-            if (steps.isEmpty()) {
-                // No vibrator matching any incoming vibration effect.
-                return Vibration.Status.IGNORED;
-            }
-            Vibration.Status status = Vibration.Status.FINISHED;
-            final int stepCount = steps.size();
-            for (int i = 0; i < stepCount; i++) {
-                Step step = steps.get(i);
-                synchronized (mLock) {
-                    if (step instanceof VibrateStep) {
-                        mCurrentVibrateStep = (VibrateStep) step;
+            CombinedVibrationEffect.Sequential effect = toSequential(mVibration.getEffect());
+            int stepsPlayed = 0;
+
+            synchronized (mLock) {
+                mStepQueue.offer(new StartVibrateStep(effect));
+                Step topOfQueue;
+
+                while ((topOfQueue = mStepQueue.peek()) != null) {
+                    long waitTime = topOfQueue.calculateWaitTime();
+                    if (waitTime <= 0) {
+                        stepsPlayed += mStepQueue.consume();
                     } else {
-                        mCurrentVibrateStep = null;
+                        try {
+                            mLock.wait(waitTime);
+                        } catch (InterruptedException e) { }
+                    }
+                    if (mForceStop) {
+                        mStepQueue.cancel();
+                        return Vibration.Status.CANCELLED;
                     }
                 }
-                status = step.play();
-                if (status != Vibration.Status.FINISHED) {
-                    // This step was ignored by the vibrators, probably effects were unsupported.
-                    break;
-                }
-                if (mForceStop) {
-                    break;
-                }
             }
-            if (mForceStop) {
-                return Vibration.Status.CANCELLED;
-            }
-            return status;
+
+            // Some effects might be ignored because the specified vibrator don't exist or doesn't
+            // support the effect. We only report ignored here if nothing was played besides the
+            // StartVibrateStep (which means every attempt to turn on the vibrator was ignored).
+            return stepsPlayed > effect.getEffects().size()
+                    ? Vibration.Status.FINISHED : Vibration.Status.IGNORED_UNSUPPORTED;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
-    private List<Step> generateSteps(CombinedVibrationEffect effect) {
-        if (effect instanceof CombinedVibrationEffect.Sequential) {
-            CombinedVibrationEffect.Sequential sequential =
-                    (CombinedVibrationEffect.Sequential) effect;
-            List<Step> steps = new ArrayList<>();
-            final int sequentialEffectCount = sequential.getEffects().size();
-            for (int i = 0; i < sequentialEffectCount; i++) {
-                int delay = sequential.getDelays().get(i);
-                if (delay > 0) {
-                    steps.add(new DelayStep(delay));
-                }
-                steps.addAll(generateSteps(sequential.getEffects().get(i)));
+    private void noteVibratorOn(long duration) {
+        try {
+            if (duration <= 0) {
+                return;
             }
-            final int stepCount = steps.size();
-            for (int i = 0; i < stepCount; i++) {
-                if (steps.get(i) instanceof VibrateStep) {
-                    return steps;
-                }
+            if (duration == Long.MAX_VALUE) {
+                // Repeating duration has started. Report a fixed duration here, noteVibratorOff
+                // should be called when this is cancelled.
+                duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
             }
-            // No valid vibrate step was generated, ignore effect completely.
-            return Lists.newArrayList();
+            mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
+            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+                    mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
+                    duration);
+        } catch (RemoteException e) {
         }
-        VibrateStep vibrateStep = null;
-        if (effect instanceof CombinedVibrationEffect.Mono) {
-            vibrateStep = createVibrateStep(mapToAvailableVibrators(
-                    ((CombinedVibrationEffect.Mono) effect).getEffect()));
-        } else if (effect instanceof CombinedVibrationEffect.Stereo) {
-            vibrateStep = createVibrateStep(filterByAvailableVibrators(
-                    ((CombinedVibrationEffect.Stereo) effect).getEffects()));
-        }
-        return vibrateStep == null ? Lists.newArrayList() : Lists.newArrayList(vibrateStep);
     }
 
-    @Nullable
-    private VibrateStep createVibrateStep(SparseArray<VibrationEffect> effects) {
-        if (effects.size() == 0) {
-            return null;
+    private void noteVibratorOff() {
+        try {
+            mBatteryStatsService.noteVibratorOff(mVibration.uid);
+            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+                    mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
+                    /* duration= */ 0);
+        } catch (RemoteException e) {
         }
-        if (effects.size() == 1) {
-            // Create simplified step that handles a single vibrator.
-            return new SingleVibrateStep(mVibrators.get(effects.keyAt(0)), effects.valueAt(0));
-        }
-        return new SyncedVibrateStep(effects);
-    }
-
-    private SparseArray<VibrationEffect> mapToAvailableVibrators(VibrationEffect effect) {
-        SparseArray<VibrationEffect> mappedEffects = new SparseArray<>(mVibrators.size());
-        for (int i = 0; i < mVibrators.size(); i++) {
-            mappedEffects.put(mVibrators.keyAt(i), effect);
-        }
-        return mappedEffects;
-    }
-
-    private SparseArray<VibrationEffect> filterByAvailableVibrators(
-            SparseArray<VibrationEffect> effects) {
-        SparseArray<VibrationEffect> filteredEffects = new SparseArray<>();
-        for (int i = 0; i < effects.size(); i++) {
-            if (mVibrators.contains(effects.keyAt(i))) {
-                filteredEffects.put(effects.keyAt(i), effects.valueAt(i));
-            }
-        }
-        return filteredEffects;
     }
 
     /**
@@ -306,341 +272,240 @@
         return timing;
     }
 
-    /**
-     * Sleeps until given {@code wakeUpTime}.
-     *
-     * <p>This stops immediately when {@link #cancel()} is called.
-     *
-     * @return true if waited until wake-up time, false if it was cancelled.
-     */
-    private boolean waitUntil(long wakeUpTime) {
-        synchronized (mLock) {
-            long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
-            while (durationRemaining > 0) {
-                try {
-                    mLock.wait(durationRemaining);
-                } catch (InterruptedException e) {
-                }
-                if (mForceStop) {
-                    return false;
-                }
-                durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
-            }
+    private static CombinedVibrationEffect.Sequential toSequential(CombinedVibrationEffect effect) {
+        if (effect instanceof CombinedVibrationEffect.Sequential) {
+            return (CombinedVibrationEffect.Sequential) effect;
         }
-        return true;
+        return (CombinedVibrationEffect.Sequential) CombinedVibrationEffect.startSequential()
+                .addNext(effect)
+                .combine();
     }
 
-    /**
-     * Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}.
-     *
-     * <p>This stops immediately when {@link #cancel()} is called.
-     *
-     * @return true if finished on vibration complete, false if it was cancelled or timed out.
-     */
-    private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) {
-        synchronized (mLock) {
-            long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
-            while (!step.isVibrationComplete() && durationRemaining > 0) {
-                try {
-                    mLock.wait(durationRemaining);
-                } catch (InterruptedException e) {
-                }
-                if (mForceStop) {
-                    return false;
-                }
-                durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
-            }
-        }
-        return step.isVibrationComplete();
-    }
-
-    private void noteVibratorOn(long duration) {
-        try {
-            mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
-                    mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
-                    duration);
-        } catch (RemoteException e) {
-        }
-    }
-
-    private void noteVibratorOff() {
-        try {
-            mBatteryStatsService.noteVibratorOff(mVibration.uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
-                    mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
-                    /* duration= */ 0);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /** Represent a single synchronized step while playing a {@link CombinedVibrationEffect}. */
-    private interface Step {
-        Vibration.Status play();
-    }
-
-    /** Represent a synchronized vibration step. */
-    private interface VibrateStep extends Step {
-        /** Callback to notify a vibrator has finished playing a effect. */
-        void vibratorComplete(int vibratorId);
-
-        /** Returns true if the vibration played by this step is complete. */
-        boolean isVibrationComplete();
-    }
-
-    /** Represent a vibration on a single vibrator. */
-    private final class SingleVibrateStep implements VibrateStep {
-        private final VibratorController mVibrator;
-        private final VibrationEffect mEffect;
+    /** Queue for {@link Step Steps}, sorted by their start time. */
+    private final class StepQueue {
+        @GuardedBy("mLock")
+        private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
 
         @GuardedBy("mLock")
-        private boolean mVibrationComplete;
-
-        SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
-            mVibrator = vibrator;
-            mEffect = effect;
+        public void offer(@NonNull Step step) {
+            mNextSteps.offer(step);
         }
 
         @GuardedBy("mLock")
-        @Override
-        public boolean isVibrationComplete() {
-            return mVibrationComplete;
+        @Nullable
+        public Step peek() {
+            return mNextSteps.peek();
+        }
+
+        /**
+         * Play and remove the step at the top of this queue, and also adds the next steps
+         * generated to be played next.
+         *
+         * @return the number of steps played
+         */
+        @GuardedBy("mLock")
+        public int consume() {
+            Step nextStep = mNextSteps.poll();
+            if (nextStep != null) {
+                mNextSteps.addAll(nextStep.play());
+                return 1;
+            }
+            return 0;
+        }
+
+        /**
+         * Play and remove the step in this queue that should be anticipated by the vibrator
+         * completion callback.
+         *
+         * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
+         * first step found is played by this method, in no particular order.
+         */
+        @GuardedBy("mLock")
+        public void consumeOnVibratorComplete(int vibratorId) {
+            Iterator<Step> it = mNextSteps.iterator();
+            List<Step> nextSteps = EMPTY_STEP_LIST;
+            while (it.hasNext()) {
+                Step step = it.next();
+                if (step.shouldPlayWhenVibratorComplete(vibratorId)) {
+                    it.remove();
+                    nextSteps = step.play();
+                    break;
+                }
+            }
+            mNextSteps.addAll(nextSteps);
+        }
+
+        /**
+         * Cancel the current queue, clearing all remaining steps.
+         *
+         * <p>This will remove and trigger {@link Step#cancel()} in all steps, in order.
+         */
+        @GuardedBy("mLock")
+        public void cancel() {
+            Step step;
+            while ((step = mNextSteps.poll()) != null) {
+                step.cancel();
+            }
+        }
+    }
+
+    /**
+     * Represent a single step for playing a vibration.
+     *
+     * <p>Every step has a start time, which can be used to apply delays between steps while
+     * executing them in sequence.
+     */
+    private abstract class Step implements Comparable<Step> {
+        public final long startTime;
+
+        Step(long startTime) {
+            this.startTime = startTime;
+        }
+
+        /** Play this step, returning a (possibly empty) list of next steps. */
+        @NonNull
+        public abstract List<Step> play();
+
+        /** Cancel this pending step. */
+        public void cancel() {
+        }
+
+        /**
+         * Return true to play this step right after a vibrator has notified vibration completed,
+         * used to anticipate steps waiting on vibrator callbacks with a timeout.
+         */
+        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+            return false;
+        }
+
+        /** Returns the time in millis to wait before playing this step. */
+        public long calculateWaitTime() {
+            if (startTime == Long.MAX_VALUE) {
+                // This step don't have a predefined start time, it's just marked to be executed
+                // after all other steps have finished.
+                return 0;
+            }
+            return Math.max(0, startTime - SystemClock.uptimeMillis());
         }
 
         @Override
-        public void vibratorComplete(int vibratorId) {
-            if (mVibrator.getVibratorInfo().getId() != vibratorId) {
-                return;
-            }
-            if (mEffect instanceof VibrationEffect.OneShot
-                    || mEffect instanceof VibrationEffect.Waveform) {
-                // Oneshot and Waveform are controlled by amplitude steps, ignore callbacks.
-                return;
-            }
-            mVibrator.off();
-            synchronized (mLock) {
-                mVibrationComplete = true;
-                mLock.notify();
-            }
+        public int compareTo(Step o) {
+            return Long.compare(startTime, o.startTime);
+        }
+    }
+
+    /**
+     * Starts a sync vibration.
+     *
+     * <p>If this step has successfully started playing a vibration on any vibrator, it will always
+     * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
+     * all their individual steps.
+     *
+     * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
+     * sequential effect isn't finished yet.
+     */
+    private final class StartVibrateStep extends Step {
+        public final CombinedVibrationEffect.Sequential sequentialEffect;
+        public final int currentIndex;
+
+        StartVibrateStep(CombinedVibrationEffect.Sequential effect) {
+            this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
+        }
+
+        StartVibrateStep(long startTime, CombinedVibrationEffect.Sequential effect, int index) {
+            super(startTime);
+            sequentialEffect = effect;
+            currentIndex = index;
         }
 
         @Override
-        public Vibration.Status play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SingleVibrateStep");
+        public List<Step> play() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
+            List<Step> nextSteps = new ArrayList<>();
             long duration = -1;
             try {
                 if (DEBUG) {
-                    Slog.d(TAG, "SingleVibrateStep starting...");
+                    Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
                 }
-                long startTime = SystemClock.uptimeMillis();
-                duration = vibratePredefined(mEffect);
-
-                if (duration > 0) {
-                    noteVibratorOn(duration);
-                    // Vibration is playing with no need to control amplitudes, just wait for native
-                    // callback or timeout.
-                    if (waitForVibrationComplete(this,
-                            startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) {
-                        return Vibration.Status.FINISHED;
-                    }
-                    // Timed out or vibration cancelled. Stop vibrator anyway.
-                    mVibrator.off();
-                    return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
+                CombinedVibrationEffect effect = sequentialEffect.getEffects().get(currentIndex);
+                DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
+                if (effectMapping == null) {
+                    // Unable to map effects to vibrators, ignore this step.
+                    return nextSteps;
                 }
 
-                startTime = SystemClock.uptimeMillis();
-                AmplitudeStep amplitudeStep = vibrateWithAmplitude(mEffect, startTime);
-                if (amplitudeStep == null) {
-                    // Vibration could not be played with or without amplitude steps.
-                    return Vibration.Status.IGNORED_UNSUPPORTED;
-                }
-
-                duration = mEffect instanceof VibrationEffect.Prebaked
-                        ? ((VibrationEffect.Prebaked) mEffect).getFallbackEffect().getDuration()
-                        : mEffect.getDuration();
-                if (duration < Long.MAX_VALUE) {
-                    // Only report vibration stats if we know how long we will be vibrating.
-                    noteVibratorOn(duration);
-                }
-                while (amplitudeStep != null) {
-                    if (!waitUntil(amplitudeStep.startTime)) {
-                        mVibrator.off();
-                        return Vibration.Status.CANCELLED;
-                    }
-                    amplitudeStep.play();
-                    amplitudeStep = amplitudeStep.nextStep();
-                }
-
-                return Vibration.Status.FINISHED;
+                duration = startVibrating(effectMapping, nextSteps);
+                noteVibratorOn(duration);
             } finally {
-                if (duration > 0 && duration < Long.MAX_VALUE) {
-                    noteVibratorOff();
-                }
-                if (DEBUG) {
-                    Slog.d(TAG, "SingleVibrateStep done.");
+                // If this step triggered any vibrator then add a finish step to wait for all
+                // active vibrators to finish their individual steps before going to the next.
+                Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep();
+                if (nextStep != null) {
+                    nextSteps.add(nextStep);
                 }
                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
             }
+            return nextSteps;
         }
 
         /**
-         * Try to vibrate given effect using prebaked or composed predefined effects.
-         *
-         * @return the duration, in millis, expected for the vibration, or -1 if effect cannot be
-         * played with predefined effects.
+         * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
+         * time this method is called, or null if sequence is complete.
          */
-        private long vibratePredefined(VibrationEffect effect) {
-            if (effect instanceof VibrationEffect.Prebaked) {
-                VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
-                long duration = mVibrator.on(prebaked, mVibration.id);
-                if (duration > 0) {
-                    return duration;
-                }
-                if (prebaked.getFallbackEffect() != null) {
-                    return vibratePredefined(prebaked.getFallbackEffect());
-                }
-            } else if (effect instanceof VibrationEffect.Composed) {
-                VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
-                return mVibrator.on(composed, mVibration.id);
+        @Nullable
+        private Step nextStep() {
+            int nextIndex = currentIndex + 1;
+            if (nextIndex >= sequentialEffect.getEffects().size()) {
+                return null;
             }
-            // OneShot and Waveform effects require amplitude change after calling vibrator.on.
-            return -1;
+            long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
+            long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
+            return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex);
         }
 
-        /**
-         * Try to vibrate given effect using {@link AmplitudeStep} to control vibration amplitude.
-         *
-         * @return the {@link AmplitudeStep} to start this vibration, or {@code null} if vibration
-         * do not require amplitude control.
-         */
-        private AmplitudeStep vibrateWithAmplitude(VibrationEffect effect, long startTime) {
-            int vibratorId = mVibrator.getVibratorInfo().getId();
-            if (effect instanceof VibrationEffect.OneShot) {
-                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
-                return new AmplitudeStep(vibratorId, oneShot, startTime, startTime);
-            } else if (effect instanceof VibrationEffect.Waveform) {
-                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
-                return new AmplitudeStep(vibratorId, waveform, startTime, startTime);
-            } else if (effect instanceof VibrationEffect.Prebaked) {
-                VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
-                if (prebaked.getFallbackEffect() != null) {
-                    return vibrateWithAmplitude(prebaked.getFallbackEffect(), startTime);
-                }
+        /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
+        @Nullable
+        private DeviceEffectMap createEffectToVibratorMapping(
+                CombinedVibrationEffect effect) {
+            if (effect instanceof CombinedVibrationEffect.Mono) {
+                return new DeviceEffectMap((CombinedVibrationEffect.Mono) effect);
+            }
+            if (effect instanceof CombinedVibrationEffect.Stereo) {
+                return new DeviceEffectMap((CombinedVibrationEffect.Stereo) effect);
             }
             return null;
         }
-    }
-
-    /** Represent a synchronized vibration step on multiple vibrators. */
-    private final class SyncedVibrateStep implements VibrateStep {
-        private final SparseArray<VibrationEffect> mEffects;
-        private final long mRequiredCapabilities;
-        private final int[] mVibratorIds;
-
-        @GuardedBy("mLock")
-        private int mActiveVibratorCounter;
-
-        SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
-            mEffects = effects;
-            mActiveVibratorCounter = mEffects.size();
-            mRequiredCapabilities = calculateRequiredSyncCapabilities(effects);
-            mVibratorIds = new int[effects.size()];
-            for (int i = 0; i < effects.size(); i++) {
-                mVibratorIds[i] = effects.keyAt(i);
-            }
-        }
-
-        @GuardedBy("mLock")
-        @Override
-        public boolean isVibrationComplete() {
-            return mActiveVibratorCounter <= 0;
-        }
-
-        @Override
-        public void vibratorComplete(int vibratorId) {
-            VibrationEffect effect = mEffects.get(vibratorId);
-            if (effect == null) {
-                return;
-            }
-            if (effect instanceof VibrationEffect.OneShot
-                    || effect instanceof VibrationEffect.Waveform) {
-                // Oneshot and Waveform are controlled by amplitude steps, ignore callbacks.
-                return;
-            }
-            mVibrators.get(vibratorId).off();
-            synchronized (mLock) {
-                --mActiveVibratorCounter;
-                mLock.notify();
-            }
-        }
-
-        @Override
-        public Vibration.Status play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep");
-            long duration = -1;
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "SyncedVibrateStep starting...");
-                }
-                final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size());
-                long startTime = SystemClock.uptimeMillis();
-                duration = startVibratingSynced(startTime, nextSteps);
-
-                if (duration <= 0) {
-                    // Vibrate step failed, vibrator could not be turned on for this step.
-                    return Vibration.Status.IGNORED;
-                }
-
-                noteVibratorOn(duration);
-                while (!nextSteps.isEmpty()) {
-                    AmplitudeStep step = nextSteps.poll();
-                    if (!waitUntil(step.startTime)) {
-                        stopAllVibrators();
-                        return Vibration.Status.CANCELLED;
-                    }
-                    step.play();
-                    AmplitudeStep nextStep = step.nextStep();
-                    if (nextStep == null) {
-                        // This vibrator has finished playing the effect for this step.
-                        synchronized (mLock) {
-                            mActiveVibratorCounter--;
-                        }
-                    } else {
-                        nextSteps.add(nextStep);
-                    }
-                }
-
-                synchronized (mLock) {
-                    // All OneShot and Waveform effects have finished. Just wait for the other
-                    // effects to end via native callbacks before finishing this synced step.
-                    final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT;
-                    if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
-                        return Vibration.Status.FINISHED;
-                    }
-
-                    // Timed out or vibration cancelled. Stop all vibrators anyway.
-                    stopAllVibrators();
-                    return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
-                }
-            } finally {
-                if (duration > 0) {
-                    noteVibratorOff();
-                }
-                if (DEBUG) {
-                    Slog.d(TAG, "SyncedVibrateStep done.");
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
 
         /**
          * Starts playing effects on designated vibrators, in sync.
          *
-         * @return A positive duration, in millis, to wait for the completion of this effect.
-         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
-         * returns the duration of a single run to be used as timeout for callbacks.
+         * @param effectMapping The {@link CombinedVibrationEffect} mapped to this device vibrators
+         * @param nextSteps     An output list to accumulate the future {@link Step Steps} created
+         *                      by this method, typically one for each vibrator that has
+         *                      successfully started vibrating on this step.
+         * @return The duration, in millis, of the {@link CombinedVibrationEffect}. Repeating
+         * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
+         * have ignored all effects.
          */
-        private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
+        private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) {
+            int vibratorCount = effectMapping.size();
+            if (vibratorCount == 0) {
+                // No effect was mapped to any available vibrator.
+                return 0;
+            }
+
+            VibratorOnStep[] steps = new VibratorOnStep[vibratorCount];
+            long vibrationStartTime = SystemClock.uptimeMillis();
+            for (int i = 0; i < vibratorCount; i++) {
+                steps[i] = new VibratorOnStep(vibrationStartTime,
+                        mVibrators.get(effectMapping.vibratorIdAt(i)), effectMapping.effectAt(i));
+            }
+
+            if (steps.length == 1) {
+                // No need to prepare and trigger sync effects on a single vibrator.
+                return startVibrating(steps[0], nextSteps);
+            }
+
             // This synchronization of vibrators should be executed one at a time, even if we are
             // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
             // one set of vibrators at a time.
@@ -648,17 +513,24 @@
                 boolean hasPrepared = false;
                 boolean hasTriggered = false;
                 try {
-                    hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities,
-                            mVibratorIds);
-                    long timeout = startVibrating(startTime, nextSteps);
+                    hasPrepared = mCallbacks.prepareSyncedVibration(
+                            effectMapping.getRequiredSyncCapabilities(),
+                            effectMapping.getVibratorIds());
 
-                    // Check if preparation was successful, otherwise devices area already vibrating
-                    if (hasPrepared) {
+                    long duration = 0;
+                    for (VibratorOnStep step : steps) {
+                        duration = Math.max(duration, startVibrating(step, nextSteps));
+                    }
+
+                    // Check if sync was prepared and if any step was accepted by a vibrator,
+                    // otherwise there is nothing to trigger here.
+                    if (hasPrepared && duration > 0) {
                         hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
                     }
-                    return timeout;
+                    return duration;
                 } finally {
                     if (hasPrepared && !hasTriggered) {
+                        // Trigger has failed or all steps were ignored by the vibrators.
                         mCallbacks.cancelSyncedVibration();
                         return 0;
                     }
@@ -666,77 +538,365 @@
             }
         }
 
-        /**
-         * Starts playing effects on designated vibrators.
-         *
-         * <p>This includes the {@link VibrationEffect.OneShot} and {@link VibrationEffect.Waveform}
-         * effects, that should start in sync with all other effects in this step. The waveforms are
-         * controlled by {@link AmplitudeStep} added to the {@code nextSteps} queue.
-         *
-         * @return A positive duration, in millis, to wait for the completion of this effect.
-         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
-         * returns the duration of a single run to be used as timeout for callbacks.
-         */
-        private long startVibrating(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
-            long maxDuration = 0;
-            for (int i = 0; i < mEffects.size(); i++) {
-                VibratorController controller = mVibrators.get(mEffects.keyAt(i));
-                VibrationEffect effect = mEffects.valueAt(i);
-                maxDuration = Math.max(maxDuration,
-                        startVibrating(controller, effect, startTime, nextSteps));
+        private long startVibrating(VibratorOnStep step, List<Step> nextSteps) {
+            nextSteps.addAll(step.play());
+            return step.getDuration();
+        }
+    }
+
+    /**
+     * Finish a sync vibration started by a {@link StartVibrateStep}.
+     *
+     * <p>This only plays after all active vibrators steps have finished, and adds a {@link
+     * StartVibrateStep} to the queue if the sequential effect isn't finished yet.
+     */
+    private final class FinishVibrateStep extends Step {
+        public final StartVibrateStep startedStep;
+
+        FinishVibrateStep(StartVibrateStep startedStep) {
+            super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue.
+            this.startedStep = startedStep;
+        }
+
+        @Override
+        public List<Step> play() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
+            try {
+                if (DEBUG) {
+                    Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex);
+                }
+                noteVibratorOff();
+                Step nextStep = startedStep.nextStep();
+                return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
             }
-            return maxDuration;
+        }
+
+        @Override
+        public void cancel() {
+            noteVibratorOff();
+        }
+    }
+
+    /**
+     * Represent a step turn the vibrator on.
+     *
+     * <p>No other calls to the vibrator is made from this step, so this can be played in between
+     * calls to 'prepare' and 'trigger' for synchronized vibrations.
+     */
+    private final class VibratorOnStep extends Step {
+        public final VibratorController controller;
+        public final VibrationEffect effect;
+        private long mDuration;
+
+        VibratorOnStep(long startTime, VibratorController controller, VibrationEffect effect) {
+            super(startTime);
+            this.controller = controller;
+            this.effect = effect;
         }
 
         /**
-         * Play a single effect on a single vibrator.
-         *
-         * @return A positive duration, in millis, to wait for the completion of this effect.
-         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
-         * returns the duration of a single run to be used as timeout for callbacks.
+         * Return the duration, in millis, of this effect. Repeating waveforms return {@link
+         * Long#MAX_VALUE}. Zero or negative values indicate the vibrator has ignored this effect.
          */
-        private long startVibrating(VibratorController controller, VibrationEffect effect,
-                long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
-            int vibratorId = controller.getVibratorInfo().getId();
-            long duration;
+        public long getDuration() {
+            return mDuration;
+        }
+
+        @Override
+        public List<Step> play() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOnStep");
+            try {
+                if (DEBUG) {
+                    Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId());
+                }
+                List<Step> nextSteps = new ArrayList<>();
+                mDuration = startVibrating(effect, nextSteps);
+                return nextSteps;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
+        private long startVibrating(VibrationEffect effect, List<Step> nextSteps) {
+            final long duration;
+            final long now = SystemClock.uptimeMillis();
             if (effect instanceof VibrationEffect.OneShot) {
                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
                 duration = oneShot.getDuration();
+                // Do NOT set amplitude here. This might be called between prepareSynced and
+                // triggerSynced, so the vibrator is not actually turned on here.
+                // The next steps will handle the amplitude after the vibrator has turned on.
                 controller.on(duration, mVibration.id);
-                nextSteps.add(
-                        new AmplitudeStep(vibratorId, oneShot, startTime, startTime + duration));
+                nextSteps.add(new VibratorAmplitudeStep(now, controller, oneShot,
+                        now + duration + CALLBACKS_EXTRA_TIMEOUT));
             } else if (effect instanceof VibrationEffect.Waveform) {
                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
-                duration = getVibratorOnDuration(waveform, 0);
-                if (duration > 0) {
-                    // Waveform starts by turning vibrator on. Do it in this sync vibrate step.
-                    controller.on(duration, mVibration.id);
+                // Return the full duration of this waveform effect.
+                duration = waveform.getDuration();
+                long onDuration = getVibratorOnDuration(waveform, 0);
+                if (onDuration > 0) {
+                    // Do NOT set amplitude here. This might be called between prepareSynced and
+                    // triggerSynced, so the vibrator is not actually turned on here.
+                    // The next steps will handle the amplitudes after the vibrator has turned on.
+                    controller.on(onDuration, mVibration.id);
                 }
-                nextSteps.add(
-                        new AmplitudeStep(vibratorId, waveform, startTime, startTime + duration));
+                long offTime = onDuration > 0 ? now + onDuration + CALLBACKS_EXTRA_TIMEOUT : now;
+                nextSteps.add(new VibratorAmplitudeStep(now, controller, waveform, offTime));
             } else if (effect instanceof VibrationEffect.Prebaked) {
                 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
                 duration = controller.on(prebaked, mVibration.id);
-                if (duration <= 0 && prebaked.getFallbackEffect() != null) {
-                    return startVibrating(controller, prebaked.getFallbackEffect(), startTime,
-                            nextSteps);
+                if (duration > 0) {
+                    nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
+                            controller));
+                } else if (prebaked.getFallbackEffect() != null) {
+                    return startVibrating(prebaked.getFallbackEffect(), nextSteps);
                 }
             } else if (effect instanceof VibrationEffect.Composed) {
                 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
                 duration = controller.on(composed, mVibration.id);
+                if (duration > 0) {
+                    nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
+                            controller));
+                }
             } else {
                 duration = 0;
             }
             return duration;
         }
+    }
 
-        private void stopAllVibrators() {
-            for (int vibratorId : mVibratorIds) {
-                VibratorController controller = mVibrators.get(vibratorId);
-                if (controller != null) {
-                    controller.off();
+    /**
+     * Represents a step to turn the vibrator off.
+     *
+     * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
+     * and can anticipated by vibrator complete callbacks.
+     */
+    private final class VibratorOffStep extends Step {
+        public final VibratorController controller;
+
+        VibratorOffStep(long startTime, VibratorController controller) {
+            super(startTime);
+            this.controller = controller;
+        }
+
+        @Override
+        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+            return controller.getVibratorInfo().getId() == vibratorId;
+        }
+
+        @Override
+        public List<Step> play() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOffStep");
+            try {
+                stopVibrating();
+                return EMPTY_STEP_LIST;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
+        @Override
+        public void cancel() {
+            stopVibrating();
+        }
+
+        private void stopVibrating() {
+            if (DEBUG) {
+                Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+            }
+            controller.off();
+        }
+    }
+
+    /** Represents a step to change the amplitude of the vibrator. */
+    private final class VibratorAmplitudeStep extends Step {
+        public final VibratorController controller;
+        public final VibrationEffect.Waveform waveform;
+        public final int currentIndex;
+        public final long expectedVibratorStopTime;
+
+        private long mNextVibratorStopTime;
+
+        VibratorAmplitudeStep(long startTime, VibratorController controller,
+                VibrationEffect.OneShot oneShot, long expectedVibratorStopTime) {
+            this(startTime, controller,
+                    (VibrationEffect.Waveform) VibrationEffect.createWaveform(
+                            new long[]{oneShot.getDuration()}, new int[]{oneShot.getAmplitude()},
+                            /* repeat= */ -1),
+                    expectedVibratorStopTime);
+        }
+
+        VibratorAmplitudeStep(long startTime, VibratorController controller,
+                VibrationEffect.Waveform waveform, long expectedVibratorStopTime) {
+            this(startTime, controller, waveform, /* index= */ 0, expectedVibratorStopTime);
+        }
+
+        VibratorAmplitudeStep(long startTime, VibratorController controller,
+                VibrationEffect.Waveform waveform, int index, long expectedVibratorStopTime) {
+            super(startTime);
+            this.controller = controller;
+            this.waveform = waveform;
+            this.currentIndex = index;
+            this.expectedVibratorStopTime = expectedVibratorStopTime;
+            mNextVibratorStopTime = expectedVibratorStopTime;
+        }
+
+        @Override
+        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+            if (controller.getVibratorInfo().getId() == vibratorId) {
+                mNextVibratorStopTime = SystemClock.uptimeMillis();
+            }
+            return false;
+        }
+
+        @Override
+        public List<Step> play() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorAmplitudeStep");
+            try {
+                if (DEBUG) {
+                    long latency = SystemClock.uptimeMillis() - startTime;
+                    Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
+                }
+                if (waveform.getTimings()[currentIndex] == 0) {
+                    // Skip waveform entries with zero timing.
+                    return nextSteps();
+                }
+                int amplitude = waveform.getAmplitudes()[currentIndex];
+                if (amplitude == 0) {
+                    stopVibrating();
+                    return nextSteps();
+                }
+                if (startTime >= mNextVibratorStopTime) {
+                    // Vibrator has stopped. Turn vibrator back on for the duration of another
+                    // cycle before setting the amplitude.
+                    long onDuration = getVibratorOnDuration(waveform, currentIndex);
+                    if (onDuration > 0) {
+                        startVibrating(onDuration);
+                        mNextVibratorStopTime =
+                                SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
+                    }
+                }
+                changeAmplitude(amplitude);
+                return nextSteps();
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
+        @Override
+        public void cancel() {
+            stopVibrating();
+        }
+
+        private void stopVibrating() {
+            if (DEBUG) {
+                Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+            }
+            controller.off();
+            mNextVibratorStopTime = SystemClock.uptimeMillis();
+        }
+
+        private void startVibrating(long duration) {
+            if (DEBUG) {
+                Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+                        + duration + "ms");
+            }
+            controller.on(duration, mVibration.id);
+        }
+
+        private void changeAmplitude(int amplitude) {
+            if (DEBUG) {
+                Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
+                        + " to " + amplitude);
+            }
+            controller.setAmplitude(amplitude);
+        }
+
+        @NonNull
+        private List<Step> nextSteps() {
+            long nextStartTime = startTime + waveform.getTimings()[currentIndex];
+            int nextIndex = currentIndex + 1;
+            if (nextIndex >= waveform.getTimings().length) {
+                nextIndex = waveform.getRepeatIndex();
+            }
+            if (nextIndex < 0) {
+                return Arrays.asList(new VibratorOffStep(nextStartTime, controller));
+            }
+            return Arrays.asList(new VibratorAmplitudeStep(nextStartTime, controller, waveform,
+                    nextIndex, mNextVibratorStopTime));
+        }
+    }
+
+    /**
+     * Map a {@link CombinedVibrationEffect} to the vibrators available on the device.
+     *
+     * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
+     * play all of the effects in sync.
+     */
+    private final class DeviceEffectMap {
+        private final SparseArray<VibrationEffect> mVibratorEffects;
+        private final int[] mVibratorIds;
+        private final long mRequiredSyncCapabilities;
+
+        DeviceEffectMap(CombinedVibrationEffect.Mono mono) {
+            mVibratorEffects = new SparseArray<>(mVibrators.size());
+            mVibratorIds = new int[mVibrators.size()];
+            for (int i = 0; i < mVibrators.size(); i++) {
+                int vibratorId = mVibrators.keyAt(i);
+                mVibratorEffects.put(vibratorId, mono.getEffect());
+                mVibratorIds[i] = vibratorId;
+            }
+            mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+        }
+
+        DeviceEffectMap(CombinedVibrationEffect.Stereo stereo) {
+            SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
+            mVibratorEffects = new SparseArray<>();
+            for (int i = 0; i < stereoEffects.size(); i++) {
+                int vibratorId = stereoEffects.keyAt(i);
+                if (mVibrators.contains(vibratorId)) {
+                    mVibratorEffects.put(vibratorId, stereoEffects.valueAt(i));
                 }
             }
+            mVibratorIds = new int[mVibratorEffects.size()];
+            for (int i = 0; i < mVibratorEffects.size(); i++) {
+                mVibratorIds[i] = mVibratorEffects.keyAt(i);
+            }
+            mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+        }
+
+        /**
+         * Return the number of vibrators mapped to play the {@link CombinedVibrationEffect} on this
+         * device.
+         */
+        public int size() {
+            return mVibratorIds.length;
+        }
+
+        /**
+         * Return all capabilities required to play the {@link CombinedVibrationEffect} in
+         * between calls to {@link IVibratorManager#prepareSynced} and
+         * {@link IVibratorManager#triggerSynced}.
+         */
+        public long getRequiredSyncCapabilities() {
+            return mRequiredSyncCapabilities;
+        }
+
+        /** Return all vibrator ids mapped to play the {@link CombinedVibrationEffect}. */
+        public int[] getVibratorIds() {
+            return mVibratorIds;
+        }
+
+        /** Return the id of the vibrator at given index. */
+        public int vibratorIdAt(int index) {
+            return mVibratorEffects.keyAt(index);
+        }
+
+        /** Return the {@link VibrationEffect} at given index. */
+        public VibrationEffect effectAt(int index) {
+            return mVibratorEffects.valueAt(index);
         }
 
         /**
@@ -782,145 +942,4 @@
                     && (prepareCapabilities & ~capability) != 0;
         }
     }
-
-    /** Represent a step to set amplitude on a single vibrator. */
-    private final class AmplitudeStep implements Step, Comparable<AmplitudeStep> {
-        public final int vibratorId;
-        public final VibrationEffect.Waveform waveform;
-        public final int currentIndex;
-        public final long startTime;
-        public final long vibratorStopTime;
-
-        AmplitudeStep(int vibratorId, VibrationEffect.OneShot oneShot,
-                long startTime, long vibratorStopTime) {
-            this(vibratorId, (VibrationEffect.Waveform) VibrationEffect.createWaveform(
-                    new long[]{oneShot.getDuration()},
-                    new int[]{oneShot.getAmplitude()}, /* repeat= */ -1),
-                    startTime,
-                    vibratorStopTime);
-        }
-
-        AmplitudeStep(int vibratorId, VibrationEffect.Waveform waveform,
-                long startTime, long vibratorStopTime) {
-            this(vibratorId, waveform, /* index= */ 0, startTime, vibratorStopTime);
-        }
-
-        AmplitudeStep(int vibratorId, VibrationEffect.Waveform waveform,
-                int index, long startTime, long vibratorStopTime) {
-            this.vibratorId = vibratorId;
-            this.waveform = waveform;
-            this.currentIndex = index;
-            this.startTime = startTime;
-            this.vibratorStopTime = vibratorStopTime;
-        }
-
-        @Override
-        public Vibration.Status play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "AmplitudeStep starting on vibrator " + vibratorId + "...");
-                }
-                VibratorController controller = mVibrators.get(vibratorId);
-                if (currentIndex < 0) {
-                    controller.off();
-                    if (DEBUG) {
-                        Slog.d(TAG, "Vibrator turned off and finishing");
-                    }
-                    return Vibration.Status.FINISHED;
-                }
-                if (waveform.getTimings()[currentIndex] == 0) {
-                    // Skip waveform entries with zero timing.
-                    return Vibration.Status.FINISHED;
-                }
-                int amplitude = waveform.getAmplitudes()[currentIndex];
-                if (amplitude == 0) {
-                    controller.off();
-                    if (DEBUG) {
-                        Slog.d(TAG, "Vibrator turned off");
-                    }
-                    return Vibration.Status.FINISHED;
-                }
-                if (startTime >= vibratorStopTime) {
-                    // Vibrator has stopped. Turn vibrator back on for the duration of another
-                    // cycle before setting the amplitude.
-                    long onDuration = getVibratorOnDuration(waveform, currentIndex);
-                    if (onDuration > 0) {
-                        controller.on(onDuration, mVibration.id);
-                        if (DEBUG) {
-                            Slog.d(TAG, "Vibrator turned on for " + onDuration + "ms");
-                        }
-                    }
-                }
-                controller.setAmplitude(amplitude);
-                if (DEBUG) {
-                    Slog.d(TAG, "Amplitude changed to " + amplitude);
-                }
-                return Vibration.Status.FINISHED;
-            } finally {
-                if (DEBUG) {
-                    Slog.d(TAG, "AmplitudeStep done.");
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-
-        @Override
-        public int compareTo(AmplitudeStep o) {
-            return Long.compare(startTime, o.startTime);
-        }
-
-        /** Return next {@link AmplitudeStep} from this waveform, of {@code null} if finished. */
-        @Nullable
-        public AmplitudeStep nextStep() {
-            if (currentIndex < 0) {
-                // Waveform has ended, no more steps to run.
-                return null;
-            }
-            long nextStartTime = startTime + waveform.getTimings()[currentIndex];
-            int nextIndex = currentIndex + 1;
-            if (nextIndex >= waveform.getTimings().length) {
-                nextIndex = waveform.getRepeatIndex();
-            }
-            return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime,
-                    nextVibratorStopTime());
-        }
-
-        /** Return next time the vibrator will stop after this step is played. */
-        private long nextVibratorStopTime() {
-            if (currentIndex < 0 || waveform.getTimings()[currentIndex] == 0
-                    || startTime < vibratorStopTime) {
-                return vibratorStopTime;
-            }
-            return startTime + getVibratorOnDuration(waveform, currentIndex);
-        }
-    }
-
-    /** Represent a delay step with fixed duration, that starts counting when it starts playing. */
-    private final class DelayStep implements Step {
-        private final int mDelay;
-
-        DelayStep(int delay) {
-            mDelay = delay;
-        }
-
-        @Override
-        public Vibration.Status play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "DelayStep");
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
-                }
-                if (waitUntil(SystemClock.uptimeMillis() + mDelay)) {
-                    return Vibration.Status.FINISHED;
-                }
-                return Vibration.Status.CANCELLED;
-            } finally {
-                if (DEBUG) {
-                    Slog.d(TAG, "DelayStep done.");
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index eaba083..e3dc70b 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -54,50 +54,6 @@
         void onComplete(int vibratorId, long vibrationId);
     }
 
-    /**
-     * Initializes the native part of this controller, creating a global reference to given
-     * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
-     * wrapper is responsible for deleting this pointer by calling the method pointed
-     * by {@link #vibratorGetFinalizer()}.
-     *
-     * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
-     * do not hold any strong reference to the instance responsible for deleting the returned
-     * pointer, to avoid creating a cyclic GC root reference.
-     */
-    static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener);
-
-    /**
-     * Returns pointer to native function responsible for cleaning up the native pointer allocated
-     * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}.
-     */
-    static native long vibratorGetFinalizer();
-
-    static native boolean vibratorIsAvailable(long nativePtr);
-
-    static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId);
-
-    static native void vibratorOff(long nativePtr);
-
-    static native void vibratorSetAmplitude(long nativePtr, int amplitude);
-
-    static native int[] vibratorGetSupportedEffects(long nativePtr);
-
-    static native int[] vibratorGetSupportedPrimitives(long nativePtr);
-
-    static native long vibratorPerformEffect(
-            long nativePtr, long effect, long strength, long vibrationId);
-
-    static native long vibratorPerformComposedEffect(
-            long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
-
-    static native void vibratorSetExternalControl(long nativePtr, boolean enabled);
-
-    static native long vibratorGetCapabilities(long nativePtr);
-
-    static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength);
-
-    static native void vibratorAlwaysOnDisable(long nativePtr, long id);
-
     VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
         this(vibratorId, listener, new NativeWrapper());
     }
@@ -109,7 +65,8 @@
         mNativeWrapper.init(vibratorId, listener);
 
         mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(),
-                nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives());
+                nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(),
+                nativeWrapper.getResonantFrequency(), nativeWrapper.getQFactor());
     }
 
     /** Register state listener for this vibrator. */
@@ -336,13 +293,47 @@
     /** Wrapper around the static-native methods of {@link VibratorController} for tests. */
     @VisibleForTesting
     public static class NativeWrapper {
+        /**
+         * Initializes the native part of this controller, creating a global reference to given
+         * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
+         * wrapper is responsible for deleting this pointer by calling the method pointed
+         * by {@link #getNativeFinalizer()}.
+         *
+         * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
+         * do not hold any strong reference to the instance responsible for deleting the returned
+         * pointer, to avoid creating a cyclic GC root reference.
+         */
+        private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener);
+
+        /**
+         * Returns pointer to native function responsible for cleaning up the native pointer
+         * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
+         */
+        private static native long getNativeFinalizer();
+        private static native boolean isAvailable(long nativePtr);
+        private static native void on(long nativePtr, long milliseconds, long vibrationId);
+        private static native void off(long nativePtr);
+        private static native void setAmplitude(long nativePtr, int amplitude);
+        private static native int[] getSupportedEffects(long nativePtr);
+        private static native int[] getSupportedPrimitives(long nativePtr);
+        private static native long performEffect(
+                long nativePtr, long effect, long strength, long vibrationId);
+        private static native long performComposedEffect(long nativePtr,
+                VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
+        private static native void setExternalControl(long nativePtr, boolean enabled);
+        private static native long getCapabilities(long nativePtr);
+        private static native void alwaysOnEnable(long nativePtr, long id, long effect,
+                long strength);
+        private static native void alwaysOnDisable(long nativePtr, long id);
+        private static native float getResonantFrequency(long nativePtr);
+        private static native float getQFactor(long nativePtr);
 
         private long mNativePtr = 0;
 
         /** Initializes native controller and allocation registry to destroy native instances. */
         public void init(int vibratorId, OnVibrationCompleteListener listener) {
-            mNativePtr = VibratorController.vibratorInit(vibratorId, listener);
-            long finalizerPtr = VibratorController.vibratorGetFinalizer();
+            mNativePtr = nativeInit(vibratorId, listener);
+            long finalizerPtr = getNativeFinalizer();
 
             if (finalizerPtr != 0) {
                 NativeAllocationRegistry registry =
@@ -354,65 +345,73 @@
 
         /** Check if the vibrator is currently available. */
         public boolean isAvailable() {
-            return VibratorController.vibratorIsAvailable(mNativePtr);
+            return isAvailable(mNativePtr);
         }
 
         /** Turns vibrator on for given time. */
         public void on(long milliseconds, long vibrationId) {
-            VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId);
+            on(mNativePtr, milliseconds, vibrationId);
         }
 
         /** Turns vibrator off. */
         public void off() {
-            VibratorController.vibratorOff(mNativePtr);
+            off(mNativePtr);
         }
 
         /** Sets the amplitude for the vibrator to run. */
         public void setAmplitude(int amplitude) {
-            VibratorController.vibratorSetAmplitude(mNativePtr, amplitude);
+            setAmplitude(mNativePtr, amplitude);
         }
 
         /** Returns all predefined effects supported by the device vibrator. */
         public int[] getSupportedEffects() {
-            return VibratorController.vibratorGetSupportedEffects(mNativePtr);
+            return getSupportedEffects(mNativePtr);
         }
 
         /** Returns all compose primitives supported by the device vibrator. */
         public int[] getSupportedPrimitives() {
-            return VibratorController.vibratorGetSupportedPrimitives(mNativePtr);
+            return getSupportedPrimitives(mNativePtr);
         }
 
         /** Turns vibrator on to perform one of the supported effects. */
         public long perform(long effect, long strength, long vibrationId) {
-            return VibratorController.vibratorPerformEffect(
-                    mNativePtr, effect, strength, vibrationId);
+            return performEffect(mNativePtr, effect, strength, vibrationId);
         }
 
         /** Turns vibrator on to perform one of the supported composed effects. */
         public long compose(
                 VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
-            return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
-                    vibrationId);
+            return performComposedEffect(mNativePtr, effect, vibrationId);
         }
 
         /** Enabled the device vibrator to be controlled by another service. */
         public void setExternalControl(boolean enabled) {
-            VibratorController.vibratorSetExternalControl(mNativePtr, enabled);
+            setExternalControl(mNativePtr, enabled);
         }
 
         /** Returns all capabilities of the device vibrator. */
         public long getCapabilities() {
-            return VibratorController.vibratorGetCapabilities(mNativePtr);
+            return getCapabilities(mNativePtr);
         }
 
         /** Enable always-on vibration with given id and effect. */
         public void alwaysOnEnable(long id, long effect, long strength) {
-            VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength);
+            alwaysOnEnable(mNativePtr, id, effect, strength);
         }
 
         /** Disable always-on vibration for given id. */
         public void alwaysOnDisable(long id) {
-            VibratorController.vibratorAlwaysOnDisable(mNativePtr, id);
+            alwaysOnDisable(mNativePtr, id);
+        }
+
+        /** Gets the vibrator's resonant frequency (F0) */
+        public float getResonantFrequency() {
+            return getResonantFrequency(mNativePtr);
+        }
+
+        /** Gets the vibrator's Q factor */
+        public float getQFactor() {
+            return getQFactor(mNativePtr);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index aeeabe2..db3d7ad 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -137,6 +137,7 @@
 import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
 import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
 import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
 import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
 import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
 import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
@@ -2114,7 +2115,7 @@
                     }
                 }
                 if (abort) {
-                    surface.remove();
+                    surface.remove(false /* prepareAnimation */);
                 }
             } else {
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s",
@@ -2128,7 +2129,8 @@
     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated,
             TaskSnapshot snapshot) {
-        if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+        if ((newTask || !processRunning || (taskSwitch && !activityCreated))
+                && !isActivityTypeHome()) {
             return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
         } else if (taskSwitch && allowTaskSnapshot) {
             if (isSnapshotCompatible(snapshot)) {
@@ -2179,7 +2181,6 @@
                         + ActivityRecord.this + " state " + mTransferringSplashScreenState);
                 if (isTransferringSplashScreen()) {
                     mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
-                    // TODO show default exit splash screen animation
                     removeStartingWindow();
                 }
             }
@@ -2196,6 +2197,9 @@
     }
 
     private boolean transferSplashScreenIfNeeded() {
+        if (!mWmService.mStartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+            return false;
+        }
         if (!mHandleExitSplashScreen || mStartingSurface == null || mStartingWindow == null
                 || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
             return false;
@@ -2265,10 +2269,14 @@
         }
         // no matter what, remove the starting window.
         mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
-        removeStartingWindow();
+        removeStartingWindowAnimation(false /* prepareAnimation */);
     }
 
     void removeStartingWindow() {
+        removeStartingWindowAnimation(true /* prepareAnimation */);
+    }
+
+    void removeStartingWindowAnimation(boolean prepareAnimation) {
         if (transferSplashScreenIfNeeded()) {
             return;
         }
@@ -2313,7 +2321,7 @@
         mWmService.mAnimationHandler.post(() -> {
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
             try {
-                surface.remove();
+                surface.remove(prepareAnimation);
             } catch (Exception e) {
                 Slog.w(TAG_WM, "Exception when removing starting window", e);
             }
@@ -6190,7 +6198,7 @@
             // Remove orphaned starting window.
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
             mStartingWindowState = STARTING_WINDOW_REMOVED;
-            removeStartingWindow();
+            removeStartingWindowAnimation(false /* prepareAnimation */);
         }
         if (isState(INITIALIZING) && !shouldBeVisible(
                 true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
@@ -8255,6 +8263,7 @@
             proto.write(PROC_ID, app.getPid());
         }
         proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
+        proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d4eedf1..52d110c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -557,7 +557,7 @@
     boolean mSupportsPictureInPicture;
     boolean mSupportsMultiDisplay;
     boolean mForceResizableActivities;
-    boolean mSupportsNonResizableMultiWindow;
+    volatile boolean mSupportsNonResizableMultiWindow;
 
     final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
 
@@ -3432,6 +3432,11 @@
     }
 
     @Override
+    public boolean supportsNonResizableMultiWindow() {
+        return mSupportsNonResizableMultiWindow;
+    }
+
+    @Override
     public boolean updateConfiguration(Configuration values) {
         mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
 
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index 13295e8..128d452 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -22,12 +22,17 @@
 import android.os.RemoteException;
 import android.view.ICrossWindowBlurEnabledListener;
 
+import com.android.internal.annotations.GuardedBy;
+
 final class BlurController {
 
     private final RemoteCallbackList<ICrossWindowBlurEnabledListener>
             mBlurEnabledListeners = new RemoteCallbackList<>();
     private final Object mLock = new Object();
+    @GuardedBy("mLock")
     boolean mBlurEnabled;
+    @GuardedBy("mLock")
+    boolean mBlurForceDisabled;
 
     BlurController() {
         mBlurEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
@@ -46,19 +51,24 @@
         mBlurEnabledListeners.unregister(listener);
     }
 
-    private void updateBlurEnabled() {
-        // TODO: add other factors disabling blurs
-        final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
+    void setForceCrossWindowBlurDisabled(boolean disable) {
         synchronized (mLock) {
-            if (mBlurEnabled == newEnabled) {
-                return;
-            }
-            mBlurEnabled = newEnabled;
-            notifyBlurEnabledChanged(newEnabled);
+            mBlurForceDisabled = disable;
+            updateBlurEnabledLocked();
         }
+
     }
 
-    private void notifyBlurEnabledChanged(boolean enabled) {
+    private void updateBlurEnabledLocked() {
+        final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled;
+        if (mBlurEnabled == newEnabled) {
+            return;
+        }
+        mBlurEnabled = newEnabled;
+        notifyBlurEnabledChangedLocked(newEnabled);
+    }
+
+    private void notifyBlurEnabledChangedLocked(boolean enabled) {
         int i = mBlurEnabledListeners.beginBroadcast();
         while (i > 0) {
             i--;
@@ -71,6 +81,4 @@
         }
         mBlurEnabledListeners.finishBroadcast();
     }
-
-
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1d45c6e..9f60878 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -131,7 +131,6 @@
 import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
@@ -1811,11 +1810,6 @@
             w.mReportOrientationChanged = true;
         }, true /* traverseTopToBottom */);
 
-        if (rotateSeamlessly) {
-            mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
-                    this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
-        }
-
         for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
             final WindowManagerService.RotationWatcher rotationWatcher
                     = mWmService.mRotationWatchers.get(i);
@@ -1868,6 +1862,7 @@
         // Update application display metrics.
         final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
         final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
+        final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
 
         final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
                 displayCutout);
@@ -1884,6 +1879,7 @@
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
         }
         mDisplayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;
+        mDisplayInfo.roundedCorners = roundedCorners;
         mDisplayInfo.getAppMetrics(mDisplayMetrics);
         if (mDisplayScalingDisabled) {
             mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 7e16c22..1262dee 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
 package com.android.server.wm;
 
 import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
-import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
+import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,7 +34,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -45,16 +46,21 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.service.displayhash.DisplayHashParams;
 import android.service.displayhash.DisplayHasherService;
 import android.service.displayhash.IDisplayHasherService;
+import android.util.Size;
 import android.util.Slog;
 import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -78,12 +84,15 @@
     private final Context mContext;
 
     /**
-     * Lock used for the cached {@link #mHashAlgorithms} array
+     * Lock used for the cached {@link #mDisplayHashAlgorithms} map
      */
-    private final Object mHashAlgorithmsLock = new Object();
+    private final Object mDisplayHashAlgorithmsLock = new Object();
 
-    @GuardedBy("mHashingAlgorithmsLock")
-    private String[] mHashAlgorithms;
+    /**
+     * The cached map of display hash algorithms to the {@link DisplayHashParams}
+     */
+    @GuardedBy("mDisplayHashAlgorithmsLock")
+    private Map<String, DisplayHashParams> mDisplayHashAlgorithms;
 
     private final Handler mHandler;
 
@@ -104,34 +113,8 @@
     }
 
     String[] getSupportedHashAlgorithms() {
-        // We have a separate lock for the hashing algorithm array since it doesn't need to make
-        // the request through the service connection. Instead, we have a lock to ensure we can
-        // properly cache the hashing algorithms array so we don't need to call into the
-        // ExtServices process for each request.
-        synchronized (mHashAlgorithmsLock) {
-            // Already have cached values
-            if (mHashAlgorithms != null) {
-                return mHashAlgorithms;
-            }
-
-            final ServiceInfo serviceInfo = getServiceInfo();
-            if (serviceInfo == null) return null;
-
-            final PackageManager pm = mContext.getPackageManager();
-            final Resources res;
-            try {
-                res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(TAG, "Error getting application resources for " + serviceInfo, e);
-                return null;
-            }
-
-            final int resourceId = serviceInfo.metaData.getInt(
-                    SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
-            mHashAlgorithms = res.getStringArray(resourceId);
-
-            return mHashAlgorithms;
-        }
+        Map<String, DisplayHashParams> displayHashAlgorithms = getDisplayHashAlgorithms();
+        return displayHashAlgorithms.keySet().toArray(new String[0]);
     }
 
     @Nullable
@@ -148,13 +131,76 @@
         return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
     }
 
-    void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
+    private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
             String hashAlgorithm, RemoteCallback callback) {
         connectAndRun(
                 service -> service.generateDisplayHash(mSalt, buffer, bounds, hashAlgorithm,
                         callback));
     }
 
+    void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
+            Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+        final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
+        DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
+        if (displayHashParams == null) {
+            Slog.w(TAG, "Failed to generateDisplayHash. Invalid hashAlgorithm");
+            sendDisplayHashError(callback, DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM);
+            return;
+        }
+
+        Size size = displayHashParams.getBufferSize();
+        if (size != null && (size.getWidth() > 0 || size.getHeight() > 0)) {
+            args.setFrameScale((float) size.getWidth() / boundsInWindow.width(),
+                    (float) size.getHeight() / boundsInWindow.height());
+        }
+
+        args.setGrayscale(displayHashParams.isGrayscaleBuffer());
+
+        SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
+                SurfaceControl.captureLayers(args.build());
+        if (screenshotHardwareBuffer == null
+                || screenshotHardwareBuffer.getHardwareBuffer() == null) {
+            Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
+            sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
+            return;
+        }
+
+        generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow,
+                hashAlgorithm, callback);
+    }
+
+    private Map<String, DisplayHashParams> getDisplayHashAlgorithms() {
+        // We have a separate lock for the hashing params to ensure we can properly cache the
+        // hashing params so we don't need to call into the ExtServices process for each request.
+        synchronized (mDisplayHashAlgorithmsLock) {
+            if (mDisplayHashAlgorithms != null) {
+                return mDisplayHashAlgorithms;
+            }
+
+            final SyncCommand syncCommand = new SyncCommand();
+            Bundle results = syncCommand.run((service, remoteCallback) -> {
+                try {
+                    service.getDisplayHashAlgorithms(remoteCallback);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to invoke getDisplayHashAlgorithms command", e);
+                }
+            });
+
+            mDisplayHashAlgorithms = new HashMap<>(results.size());
+            for (String key : results.keySet()) {
+                mDisplayHashAlgorithms.put(key, results.getParcelable(key));
+            }
+
+            return mDisplayHashAlgorithms;
+        }
+    }
+
+    void sendDisplayHashError(RemoteCallback callback, int errorCode) {
+        Bundle bundle = new Bundle();
+        bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
+        callback.sendResult(bundle);
+    }
+
     /**
      * Calculate the bounds to generate the hash for. This takes into account window transform,
      * magnification, and display bounds.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 32152ec..01f0359 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1410,9 +1410,9 @@
             boolean localClient) {
         final InsetsState state =
                 mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
-        final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
-        outInsetsState.set(state, inSizeCompatMode || localClient);
-        if (inSizeCompatMode) {
+        final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken);
+        outInsetsState.set(state, hasCompatScale || localClient);
+        if (hasCompatScale) {
             final float compatScale = windowToken != null
                     ? windowToken.getSizeCompatScale()
                     : mDisplayContent.mCompatibleScreenScale;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 63cb38a..5df1355 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -567,7 +567,7 @@
         }
         mDisplayContent.forAllWindows(w -> {
             if (w.mSeamlesslyRotated) {
-                w.finishSeamlessRotation(false /* timeout */);
+                w.cancelSeamlessRotation();
                 w.mSeamlesslyRotated = false;
             }
         }, true /* traverseTopToBottom */);
@@ -670,24 +670,6 @@
         }
     }
 
-    void onSeamlessRotationTimeout() {
-        final boolean[] isLayoutNeeded = { false };
-
-        mDisplayContent.forAllWindows(w -> {
-            if (!w.mSeamlesslyRotated) {
-                return;
-            }
-            isLayoutNeeded[0] = true;
-            w.setDisplayLayoutNeeded();
-            w.finishSeamlessRotation(true /* timeout */);
-            markForSeamlessRotation(w, false /* seamlesslyRotated */);
-        }, true /* traverseTopToBottom */);
-
-        if (isLayoutNeeded[0]) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
     /**
      * Returns the animation to run for a rotation transition based on the top fullscreen windows
      * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 999c585..62e4a85 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -16,12 +16,15 @@
 
 package com.android.server.wm;
 
+import static java.lang.Integer.toHexString;
+
 import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.view.IDragAndDropPermissions;
 import com.android.server.LocalServices;
@@ -32,6 +35,9 @@
 class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
         implements IBinder.DeathRecipient {
 
+    private static final String TAG = "DragAndDrop";
+    private static final boolean DEBUG = false;
+
     private final WindowManagerGlobalLock mGlobalLock;
     private final int mSourceUid;
     private final String mTargetPackage;
@@ -43,7 +49,7 @@
 
     private IBinder mActivityToken = null;
     private IBinder mPermissionOwnerToken = null;
-    private IBinder mTransientToken = null;
+    private IBinder mAppToken = null;
 
     DragAndDropPermissionsHandler(WindowManagerGlobalLock lock, ClipData clipData, int sourceUid,
             String targetPackage, int mode, int sourceUserId, int targetUserId) {
@@ -62,6 +68,10 @@
         if (mActivityToken != null || mPermissionOwnerToken != null) {
             return;
         }
+        if (DEBUG) {
+            Log.d(TAG, this + ": taking permissions bound to activity: "
+                    + toHexString(activityToken.hashCode()));
+        }
         mActivityToken = activityToken;
 
         // Will throw if Activity is not found.
@@ -84,14 +94,18 @@
     }
 
     @Override
-    public void takeTransient(IBinder transientToken) throws RemoteException {
+    public void takeTransient(IBinder appToken) throws RemoteException {
         if (mActivityToken != null || mPermissionOwnerToken != null) {
             return;
         }
+        if (DEBUG) {
+            Log.d(TAG, this + ": taking permissions bound to app process: "
+                    + toHexString(appToken.hashCode()));
+        }
         mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
                 .newUriPermissionOwner("drop");
-        mTransientToken = transientToken;
-        mTransientToken.linkToDeath(this, 0);
+        mAppToken = appToken;
+        mAppToken.linkToDeath(this, 0);
 
         doTake(mPermissionOwnerToken);
     }
@@ -112,11 +126,17 @@
             } finally {
                 mActivityToken = null;
             }
+            if (DEBUG) {
+                Log.d(TAG, this + ": releasing activity-bound permissions");
+            }
         } else {
             permissionOwner = mPermissionOwnerToken;
             mPermissionOwnerToken = null;
-            mTransientToken.unlinkToDeath(this, 0);
-            mTransientToken = null;
+            mAppToken.unlinkToDeath(this, 0);
+            mAppToken = null;
+            if (DEBUG) {
+                Log.d(TAG, this + ": releasing process-bound permissions");
+            }
         }
 
         UriGrantsManagerInternal ugm = LocalServices.getService(UriGrantsManagerInternal.class);
@@ -139,6 +159,9 @@
 
     @Override
     public void binderDied() {
+        if (DEBUG) {
+            Log.d(TAG, this + ": app process died: " + toHexString(mAppToken.hashCode()));
+        }
         try {
             release();
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 627af91..1120a07 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -182,8 +182,6 @@
                     if (SHOW_LIGHT_TRANSACTIONS) {
                         Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
                     }
-
-                    mDragState.notifyLocationLocked(touchX, touchY);
                 } finally {
                     if (surface != null) {
                         surface.release();
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0b3c065..08d5e80 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -515,50 +515,6 @@
         mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply();
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: pos=(%d,%d)", mSurfaceControl,
                 (int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY));
-
-        notifyLocationLocked(x, y);
-    }
-
-    void notifyLocationLocked(float x, float y) {
-        // Tell the affected window
-        WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
-        if (touchedWin != null && !isWindowNotified(touchedWin)) {
-            // The drag point is over a window which was not notified about a drag start.
-            // Pretend it's over empty space.
-            touchedWin = null;
-        }
-
-        try {
-            final int myPid = Process.myPid();
-
-            // have we dragged over a new window?
-            if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
-                if (DEBUG_DRAG) {
-                    Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow);
-                }
-                // force DRAG_EXITED_EVENT if appropriate
-                DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
-                        0, 0, 0, 0, null, null, null, null, null, false);
-                mTargetWindow.mClient.dispatchDragEvent(evt);
-                if (myPid != mTargetWindow.mSession.mPid) {
-                    evt.recycle();
-                }
-            }
-            if (touchedWin != null) {
-                if (false && DEBUG_DRAG) {
-                    Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin);
-                }
-                DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
-                        x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null, false);
-                touchedWin.mClient.dispatchDragEvent(evt);
-                if (myPid != touchedWin.mSession.mPid) {
-                    evt.recycle();
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG_WM, "can't send drag notification to windows");
-        }
-        mTargetWindow = touchedWin;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
index a1e3ac7..aa73170 100644
--- a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
@@ -37,10 +37,15 @@
         super(displayContent);
         final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
         mStatusBar = displayPolicy.getStatusBar();
-        // Do not animate movable navigation bar (e.g. non-gesture mode).
+
+        final RecentsAnimationController controller =
+                displayContent.mWmService.getRecentsAnimationController();
+        final boolean navBarControlledByRecents =
+                controller != null && controller.isNavigationBarAttachedToApp();
+        // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation bar
+        // is currently controlled by recents animation.
         mNavigationBar = !displayPolicy.navigationBarCanMove()
-                ? displayPolicy.getNavigationBar()
-                : null;
+                && !navBarControlledByRecents ? displayPolicy.getNavigationBar() : null;
     }
 
     /** Applies show animation on the previously hidden window tokens. */
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index c6c7fe0..45c4233 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -93,7 +93,6 @@
     private boolean mServerVisible;
 
     private boolean mSeamlessRotating;
-    private long mFinishSeamlessRotateFrameNumber = -1;
 
     private final boolean mControllable;
 
@@ -342,15 +341,6 @@
         mIsLeashReadyForDispatching = false;
 
         final SurfaceControl leash = mAdapter.mCapturedLeash;
-        final long frameNumber = mFinishSeamlessRotateFrameNumber;
-        mFinishSeamlessRotateFrameNumber = -1;
-        if (mWin.mHasSurface && leash != null) {
-            // We just finished the seamless rotation. We don't want to change the position or the
-            // window crop of the surface controls (including the leash) until the client finishes
-            // drawing the new frame of the new orientation. Although we cannot defer the reparent
-            // operation, it is fine, because reparent won't cause any visual effect.
-            deferTransactionUntil(t, leash, frameNumber);
-        }
         mControlTarget = target;
         updateVisibility();
         mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition);
@@ -359,19 +349,14 @@
     }
 
     void startSeamlessRotation() {
-        if (!mSeamlessRotating) {
-            mSeamlessRotating = true;
-
-            // This will revoke the leash and clear the control target.
-            mWin.cancelAnimation();
-        }
+      if (!mSeamlessRotating) {
+          mSeamlessRotating = true;
+          mWin.cancelAnimation();
+      }
     }
 
-    void finishSeamlessRotation(boolean timeout) {
-        if (mSeamlessRotating) {
-            mSeamlessRotating = false;
-            mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber();
-        }
+    void finishSeamlessRotation() {
+        mSeamlessRotating = false;
     }
 
     boolean updateClientVisibility(InsetsControlTarget caller) {
@@ -529,7 +514,6 @@
         proto.write(CLIENT_VISIBLE, mClientVisible);
         proto.write(SERVER_VISIBLE, mServerVisible);
         proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
-        proto.write(FINISH_SEAMLESS_ROTATE_FRAME_NUMBER, mFinishSeamlessRotateFrameNumber);
         proto.write(CONTROLLABLE, mControllable);
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e02cce4..64ff108 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -17,7 +17,13 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -104,7 +110,8 @@
     public @interface ReorderMode {}
 
     private final WindowManagerService mService;
-    private final StatusBarManagerInternal mStatusBar;
+    @VisibleForTesting
+    final StatusBarManagerInternal mStatusBar;
     private IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
@@ -149,6 +156,7 @@
 
     @VisibleForTesting
     boolean mShouldAttachNavBarToAppDuringTransition;
+    private boolean mNavigationBarAttachedToApp;
 
     /**
      * Animates the screenshot of task that used to be controlled by RecentsAnimation.
@@ -225,7 +233,8 @@
         }
 
         @Override
-        public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+        public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
+                float[] float9) {
             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                     "setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds);
             final long token = Binder.clearCallingIdentity();
@@ -235,6 +244,8 @@
                         final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
                         if (taskAdapter.mTask.mTaskId == taskId) {
                             taskAdapter.mFinishBounds.set(destinationBounds);
+                            taskAdapter.mFinishWindowCrop.set(windowCrop);
+                            taskAdapter.mFinishTransform = float9;
                             break;
                         }
                     }
@@ -369,7 +380,17 @@
         }
 
         @Override
-        public void detachNavigationBarFromApp() {}
+        public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    restoreNavigationBarFromApp(moveHomeToTop);
+                    mService.mWindowPlacerLocked.requestTraversal();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     /**
@@ -440,9 +461,7 @@
             return;
         }
 
-        if (mShouldAttachNavBarToAppDuringTransition) {
-            attachNavBarToApp();
-        }
+        attachNavigationBarToApp();
 
         // Adjust the wallpaper visibility for the showing target activity
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
@@ -577,32 +596,52 @@
         }
     }
 
-    @VisibleForTesting
-    WindowToken getNavigationBarWindowToken() {
-        WindowState navBar = mDisplayContent.getDisplayPolicy().getNavigationBar();
-        if (navBar != null) {
-            return navBar.mToken;
-        }
-        return null;
+    boolean isNavigationBarAttachedToApp() {
+        return mNavigationBarAttachedToApp;
     }
 
-    private void attachNavBarToApp() {
+    @VisibleForTesting
+    WindowState getNavigationBarWindow() {
+        return mDisplayContent.getDisplayPolicy().getNavigationBar();
+    }
+
+    private void attachNavigationBarToApp() {
+        if (!mShouldAttachNavBarToAppDuringTransition
+                // Skip the case where the nav bar is controlled by fixed rotation.
+                || mDisplayContent.getFixedRotationAnimationController() != null) {
+            return;
+        }
         ActivityRecord topActivity = null;
+        boolean shouldTranslateNavBar = false;
+        final boolean isDisplayLandscape =
+                mDisplayContent.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
             final Task task = adapter.mTask;
-            if (!task.isHomeOrRecentsRootTask()) {
-                topActivity = task.getTopVisibleActivity();
-                break;
+            final boolean isSplitScreenSecondary =
+                    task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (task.isHomeOrRecentsRootTask()
+                    // TODO(b/178449492): Will need to update for the new split screen mode once
+                    // it's ready.
+                    // Skip if the task is the secondary split screen and in landscape.
+                    || (isSplitScreenSecondary && isDisplayLandscape)) {
+                continue;
             }
-        }
-        final WindowToken navToken = getNavigationBarWindowToken();
-        if (topActivity == null || navToken == null) {
-            return;
+            shouldTranslateNavBar = isSplitScreenSecondary;
+            topActivity = task.getTopVisibleActivity();
+            break;
         }
 
-        final SurfaceControl.Transaction t = navToken.getPendingTransaction();
-        final SurfaceControl navSurfaceControl = navToken.getSurfaceControl();
+        final WindowState navWindow = getNavigationBarWindow();
+        if (topActivity == null || navWindow == null || navWindow.mToken == null) {
+            return;
+        }
+        mNavigationBarAttachedToApp = true;
+        final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+        final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+        if (shouldTranslateNavBar) {
+            navWindow.setSurfaceTranslationY(-topActivity.getBounds().top);
+        }
         t.reparent(navSurfaceControl, topActivity.getSurfaceControl());
         t.show(navSurfaceControl);
 
@@ -613,16 +652,32 @@
             // Place the nav bar on top of anything else in the top activity.
             t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
         }
+        if (mStatusBar != null) {
+            mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, false);
+        }
     }
 
-    private void restoreNavBarFromApp(boolean animate) {
-        // Reparent the SurfaceControl of nav bar token back.
-        final WindowToken navToken = getNavigationBarWindowToken();
-        final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
-        if (navToken != null) {
-            final WindowContainer parent = navToken.getParent();
-            t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+    private void restoreNavigationBarFromApp(boolean animate) {
+        if (!mNavigationBarAttachedToApp) {
+            return;
         }
+        if (mStatusBar != null) {
+            mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, true);
+        }
+
+        final WindowState navWindow = getNavigationBarWindow();
+        if (navWindow == null) {
+            return;
+        }
+        navWindow.setSurfaceTranslationY(0);
+
+        if (navWindow.mToken == null) {
+            return;
+        }
+        final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
+        final WindowContainer parent = navWindow.mToken.getParent();
+        // Reparent the SurfaceControl of nav bar token back.
+        t.reparent(navWindow.mToken.getSurfaceControl(), parent.getSurfaceControl());
 
         if (animate) {
             // Run fade-in animation to show navigation bar back to bottom of the display.
@@ -852,9 +907,7 @@
             removeWallpaperAnimation(wallpaperAdapter);
         }
 
-        if (mShouldAttachNavBarToAppDuringTransition) {
-            restoreNavBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
-        }
+        restoreNavigationBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
 
         // Clear any pending failsafe runnables
         mService.mH.removeCallbacks(mFailsafeRunnable);
@@ -1038,6 +1091,9 @@
         private final Rect mLocalBounds = new Rect();
         // The bounds of the target when animation is finished
         private final Rect mFinishBounds = new Rect();
+        // Bounds and transform for the final transaction.
+        private final Rect mFinishWindowCrop = new Rect();
+        private float[] mFinishTransform;
 
         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
             mTask = task;
@@ -1074,13 +1130,31 @@
 
         void onCleanup() {
             if (!mFinishBounds.isEmpty()) {
-                // Apply any pending bounds changes
-                final SurfaceControl taskSurface = mTask.getSurfaceControl();
-                mTask.getPendingTransaction()
-                        .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top)
-                        .setWindowCrop(taskSurface, mFinishBounds.width(), mFinishBounds.height())
+                final SurfaceControl taskSurface = mTask.mSurfaceControl;
+                final Transaction pendingTransaction = mTask.getPendingTransaction();
+                if (mFinishTransform != null) {
+                    pendingTransaction
+                            .setMatrix(taskSurface,
+                                    mFinishTransform[MSCALE_X], mFinishTransform[MSKEW_Y],
+                                    mFinishTransform[MSKEW_X], mFinishTransform[MSCALE_Y]);
+                }
+                float left = mFinishBounds.left;
+                float top = mFinishBounds.top;
+                if (!mFinishWindowCrop.isEmpty()) {
+                    pendingTransaction.setWindowCrop(taskSurface, mFinishWindowCrop);
+                    if (mFinishTransform != null) {
+                        // adjust the position for insets.
+                        left -= mFinishWindowCrop.left * mFinishTransform[MSCALE_X];
+                        top -= mFinishWindowCrop.top * mFinishTransform[MSCALE_Y];
+                    }
+                }
+                pendingTransaction
+                        .setPosition(taskSurface, left, top)
                         .apply();
                 mTask.mLastRecentsAnimationBounds.set(mFinishBounds);
+                // reset the variables
+                mFinishTransform = null;
+                mFinishWindowCrop.setEmpty();
                 mFinishBounds.setEmpty();
             } else if (!mTask.isAttached()) {
                 // Apply the task's pending transaction in case it is detached and its transaction
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 42cb96f..6fc585e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -104,11 +104,11 @@
      */
     void goodToGo(@WindowManager.TransitionOldType int transit) {
         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
-        if (mPendingAnimations.isEmpty() || mCanceled) {
+        if (mCanceled) {
             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
-                    "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
-                    mCanceled, mPendingAnimations.size());
+                    "goodToGo(): Animation canceled already");
             onAnimationFinished();
+            invokeAnimationCancelled();
             return;
         }
 
@@ -120,8 +120,11 @@
         // Create the app targets
         final RemoteAnimationTarget[] appTargets = createAppAnimations();
         if (appTargets.length == 0) {
-            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");
+            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
+                    "goodToGo(): No apps to animate, mPendingAnimations=%d",
+                    mPendingAnimations.size());
             onAnimationFinished();
+            invokeAnimationCancelled();
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 1212302..2a768e7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2818,7 +2818,7 @@
      * @param launchParams   The resolved launch params to use.
      * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
      * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
-     * @return The roott task to use for the launch or INVALID_TASK_ID.
+     * @return The root task to use for the launch or INVALID_TASK_ID.
      */
     Task getLaunchRootTask(@Nullable ActivityRecord r,
             @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop,
@@ -2887,7 +2887,7 @@
                 // Falling back to default task container
                 taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea();
                 rootTask = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
-                        activityType, onTop);
+                        launchParams, activityType, onTop);
                 if (rootTask != null) {
                     return rootTask;
                 }
@@ -2942,7 +2942,8 @@
             }
         }
 
-        return container.getOrCreateRootTask(r, options, candidateTask, activityType, onTop);
+        return container.getOrCreateRootTask(
+                r, options, candidateTask, launchParams, activityType, onTop);
     }
 
     /** @return true if activity record is null or can be launched on provided display. */
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 3d305e4..4cc369f 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -35,9 +35,6 @@
  * Helper class for seamless rotation.
  *
  * Works by transforming the {@link WindowState} back into the old display rotation.
- *
- * Uses {@link Transaction#deferTransactionUntil(SurfaceControl, IBinder, long)} instead of
- * latching on the buffer size to allow for seamless 180 degree rotations.
  */
 public class SeamlessRotator {
 
@@ -103,23 +100,12 @@
      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
      * {@link WindowState} next frame, such that they apply at the same time the client draws the
      * window in the new orientation.
-     *
-     * In the case of a rotation timeout, we want to remove the transform immediately and not defer
-     * it.
      */
-    public void finish(WindowState win, boolean timeout) {
-        final Transaction t = win.getPendingTransaction();
-        finish(t, win);
-        if (win.mWinAnimator.mSurfaceController != null && !timeout) {
-            t.deferTransactionUntil(win.mSurfaceControl,
-                    win.getClientViewRootSurface(), win.getFrameNumber());
-            t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
-                    win.getClientViewRootSurface(), win.getFrameNumber());
-        }
-    }
-
-    /** Removes the transform and restore to the original last position. */
     void finish(Transaction t, WindowContainer win) {
+        if (win.mSurfaceControl == null || !win.mSurfaceControl.isValid()) {
+            return;
+        }
+
         mTransform.reset();
         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
         t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index ef4a40f..0708569 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -40,9 +40,8 @@
     private static final String TAG = TAG_WITH_CLASS_NAME
             ? StartingSurfaceController.class.getSimpleName() : TAG_WM;
     /** Set to {@code true} to enable shell starting surface drawer. */
-    private static final boolean DEBUG_ENABLE_SHELL_DRAWER =
-            SystemProperties.getBoolean("persist.debug.shell_starting_surface", false);
-
+    static final boolean DEBUG_ENABLE_SHELL_DRAWER =
+            SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
     private final WindowManagerService mService;
 
     public StartingSurfaceController(WindowManagerService wm) {
@@ -53,8 +52,8 @@
             int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
             int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
         if (!DEBUG_ENABLE_SHELL_DRAWER) {
-            return mService.mPolicy.addSplashScreen(activity.token, packageName, theme,
-                    compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+            return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName,
+                    theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                     overrideConfig, displayId);
         }
 
@@ -139,8 +138,9 @@
         }
 
         @Override
-        public void remove() {
-            mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask);
+        public void remove(boolean animate) {
+            mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
+                    animate);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d992a45..a4b4726 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4135,6 +4135,8 @@
         final StartingWindowInfo info = new StartingWindowInfo();
         info.taskInfo = getTaskInfo();
 
+        info.isKeyguardOccluded =
+            mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
         final ActivityRecord topActivity = getTopMostActivity();
         if (topActivity != null) {
             info.startingWindowTypeParameter =
@@ -5245,17 +5247,29 @@
         if (mForceHiddenFlags == newFlags) {
             return false;
         }
+
         final boolean wasHidden = isForceHidden();
+        final boolean wasVisible = isVisible();
         mForceHiddenFlags = newFlags;
-        if (wasHidden != isForceHidden() && isTopActivityFocusable()) {
-            // The change in force-hidden state will change visibility without triggering a root
-            // task order change, so we should reset the preferred top focusable root task to ensure
-            // it's not used if a new activity is started from this task.
-            getDisplayArea().resetPreferredTopFocusableRootTaskIfNeeded(this);
+        final boolean nowHidden = isForceHidden();
+        if (wasHidden != nowHidden) {
+            final String reason = "setForceHidden";
+            if (wasVisible && nowHidden) {
+                // Move this visible task to back when the task is forced hidden
+                moveToBack(reason, null);
+            } else if (isAlwaysOnTop()) {
+                // Move this always-on-top task to front when no longer hidden
+                moveToFront(reason);
+            }
         }
         return true;
     }
 
+    @Override
+    public boolean isAlwaysOnTop() {
+        return !isForceHidden() && super.isAlwaysOnTop();
+    }
+
     /**
      * Returns whether this task is currently forced to be hidden for any reason.
      */
@@ -7482,17 +7496,22 @@
     }
 
     public void setAlwaysOnTop(boolean alwaysOnTop) {
-        if (isAlwaysOnTop() == alwaysOnTop) {
+        // {@link #isAwaysonTop} overrides the original behavior which also evaluates if this
+        // task is force hidden, so super.isAlwaysOnTop() is used here to see whether the
+        // alwaysOnTop attributes should be updated.
+        if (super.isAlwaysOnTop() == alwaysOnTop) {
             return;
         }
         super.setAlwaysOnTop(alwaysOnTop);
-        final TaskDisplayArea taskDisplayArea = getDisplayArea();
         // positionChildAtTop() must be called even when always on top gets turned off because we
         // need to make sure that the root task is moved from among always on top windows to
         // below other always on top windows. Since the position the root task should be inserted
         // into is calculated properly in {@link DisplayContent#getTopInsertPosition()} in both
         // cases, we can just request that the root task is put at top here.
-        taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
+        // Don't bother moving task to top if this task is force hidden and invisible to user.
+        if (!isForceHidden()) {
+            getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
+        }
     }
 
     void dismissPip() {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ed92fd0..498fc5c 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -57,6 +57,7 @@
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.wm.LaunchParamsController.LaunchParams;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -393,7 +394,7 @@
         final boolean moveToBottom = position <= 0;
 
         final int oldPosition = mChildren.indexOf(child);
-        if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
+        if (child.isAlwaysOnTop() && !moveToTop) {
             // This root task is always-on-top, override the default behavior.
             Slog.w(TAG_WM, "Ignoring move of always-on-top root task=" + this + " to bottom");
 
@@ -974,14 +975,6 @@
         onRootTaskOrderChanged(rootTask);
     }
 
-    /** Reset the mPreferredTopFocusableRootTask if it is or below the given task. */
-    void resetPreferredTopFocusableRootTaskIfNeeded(Task task) {
-        if (mPreferredTopFocusableRootTask != null
-                && mPreferredTopFocusableRootTask.compareTo(task) <= 0) {
-            mPreferredTopFocusableRootTask = null;
-        }
-    }
-
     /**
      * Moves/reparents `task` to the back of whatever container the root home task is in. This is
      * for when we just want to move a task to "the back" vs. a specific place. The primary use-case
@@ -1080,11 +1073,17 @@
      * @see #getOrCreateRootTask(int, int, boolean)
      */
     Task getOrCreateRootTask(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
-            boolean onTop) {
-        // First preference is the windowing mode in the activity options if set.
-        int windowingMode = (options != null)
-                ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+            @Nullable ActivityOptions options, @Nullable Task candidateTask,
+            @Nullable LaunchParams launchParams, int activityType, boolean onTop) {
+        int windowingMode = WINDOWING_MODE_UNDEFINED;
+        if (launchParams != null) {
+            // If launchParams isn't null, windowing mode is already resolved.
+            windowingMode = launchParams.mWindowingMode;
+        } else if (options != null) {
+            // If launchParams is null and options isn't let's use the windowing mode in the
+            // options.
+            windowingMode = options.getLaunchWindowingMode();
+        }
         // Validate that our desired windowingMode will work under the current conditions.
         // UNDEFINED windowing mode is a valid result and means that the new root task will inherit
         // it's display's windowing mode.
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fc6db61..5d22f8f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -32,6 +32,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -131,10 +132,28 @@
             });
         }
 
-        void removeStartingWindow(Task task) {
+        void removeStartingWindow(Task task, boolean prepareAnimation) {
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
+                SurfaceControl firstWindowLeash = null;
+                Rect mainFrame = null;
+                // TODO enable shift up animation once we fix flicker test
+//                final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+//                if (prepareAnimation && playShiftUpAnimation) {
+//                    final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+//                    if (topActivity != null) {
+//                        final WindowState mainWindow =
+//                                topActivity.findMainWindow(false/* includeStartingApp */);
+//                        if (mainWindow != null) {
+                // TODO create proper leash instead of the copied SC
+//                            firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
+//                                    "TaskOrganizerController.removeStartingWindow");
+//                            mainFrame = mainWindow.getRelativeFrame();
+//                        }
+//                    }
+//                }
                 try {
-                    mTaskOrganizer.removeStartingWindow(task.mTaskId);
+                    mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
+                            prepareAnimation);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
                 }
@@ -249,8 +268,8 @@
             mOrganizer.addStartingWindow(t, appToken, launchTheme);
         }
 
-        void removeStartingWindow(Task t) {
-            mOrganizer.removeStartingWindow(t);
+        void removeStartingWindow(Task t, boolean prepareAnimation) {
+            mOrganizer.removeStartingWindow(t, prepareAnimation);
         }
 
         void copySplashScreenView(Task t) {
@@ -495,14 +514,14 @@
         return true;
     }
 
-    void removeStartingWindow(Task task) {
+    void removeStartingWindow(Task task, boolean prepareAnimation) {
         final Task rootTask = task.getRootTask();
         if (rootTask == null || rootTask.mTaskOrganizer == null) {
             return;
         }
         final TaskOrganizerState state =
                 mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
-        state.removeStartingWindow(task);
+        state.removeStartingWindow(task, prepareAnimation);
     }
 
     boolean copySplashScreenView(Task task) {
@@ -865,6 +884,7 @@
             mPendingTaskEvents.remove(pending);
         }
         mPendingTaskEvents.add(pending);
+        mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 9d35c25..525420e 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -188,7 +188,8 @@
             transferFocusFromWin = displayContent.mCurrentFocus;
         }
         if (!mInputManager.transferTouchFocus(
-                transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel)) {
+                transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
+                false /* isDragDrop */)) {
             Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
             cleanUpTaskPositioner();
             return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b810de9..8915eba 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -37,6 +37,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.Display;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
@@ -624,7 +625,7 @@
     /**
      * Called when screen is being turned off.
      */
-    void screenTurningOff(ScreenOffListener listener) {
+    void screenTurningOff(int displayId, ScreenOffListener listener) {
         if (shouldDisableSnapshots()) {
             listener.onScreenOff();
             return;
@@ -635,7 +636,7 @@
             try {
                 synchronized (mService.mGlobalLock) {
                     mTmpTasks.clear();
-                    mService.mRoot.forAllTasks(task -> {
+                    mService.mRoot.getDisplayContent(displayId).forAllTasks(task -> {
                         // Since RecentsAnimation will handle task snapshot while switching apps
                         // with the best capture timing (e.g. IME window capture), No need
                         // additional task capture while task is controlled by RecentsAnimation.
@@ -645,7 +646,7 @@
                     });
                     // Allow taking snapshot of home when turning screen off to reduce the delay of
                     // waking from secure lock to home.
-                    final boolean allowSnapshotHome =
+                    final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY &&
                             mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId);
                     snapshotTasks(mTmpTasks, allowSnapshotHome);
                 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 07610ab..79a6bd7 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -281,13 +281,14 @@
     }
 
     @Override
-    public void remove() {
+    public void remove(boolean animate) {
         synchronized (mService.mGlobalLock) {
             final long now = SystemClock.uptimeMillis();
             if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
                     // Show the latest content as soon as possible for unlocking to home.
                     && mActivityType != ACTIVITY_TYPE_HOME) {
-                mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
+                mHandler.postAtTime(() -> remove(false /* prepareAnimation */),
+                        mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                         "Defer removing snapshot surface in %dms", (now - mShownTime));
 
@@ -517,7 +518,7 @@
                 // The orientation of the screen is changing. We better remove the snapshot ASAP as
                 // we are going to wait on the new window in any case to unfreeze the screen, and
                 // the starting window is not needed anymore.
-                sHandler.post(mOuter::remove);
+                sHandler.post(() -> mOuter.remove(false /* prepareAnimation */));
             }
             if (reportDraw) {
                 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 7450782..53ebfb2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -221,7 +221,8 @@
                 DragState state, Display display, InputManagerService service,
                 InputChannel source) {
             state.register(display);
-            return service.transferTouchFocus(source, state.getInputChannel());
+            return service.transferTouchFocus(source, state.getInputChannel(),
+                    true /* isDragDrop */);
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6f853c7..ec012fc3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -84,8 +84,6 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
-import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
-import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -273,7 +271,6 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
@@ -367,9 +364,6 @@
     /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
     static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
 
-    /** Amount of time (in milliseconds) to delay before declaring a seamless rotation timeout. */
-    static final int SEAMLESS_ROTATION_TIMEOUT_DURATION = 2000;
-
     /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
     static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
 
@@ -529,14 +523,6 @@
         @Override
         public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
                 boolean asProto) {
-            // Bugreport dumps the trace 2x, 1x as proto and 1x as text. Save file to disk only 1x.
-            if (asProto && mWindowTracing.isEnabled()) {
-                mWindowTracing.stopTrace(null, false /* writeToFile */);
-                BackgroundThread.getHandler().post(() -> {
-                    mWindowTracing.writeTraceToFile();
-                    mWindowTracing.startTrace(null);
-                });
-            }
             doDump(fd, pw, new String[] {"-a"}, asProto);
         }
 
@@ -2245,9 +2231,6 @@
             win.setFrameNumber(frameNumber);
 
             final DisplayContent dc = win.getDisplayContent();
-            if (!dc.mWaitingForConfig) {
-                win.finishSeamlessRotation(false /* timeout */);
-            }
 
             if (win.mPendingPositionChanged != null) {
                 win.mPendingPositionChanged.updateLeashPosition(frameNumber);
@@ -2255,6 +2238,7 @@
             }
 
             if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) {
+                win.prepareDrawHandlers();
                 result |= RELAYOUT_RES_BLAST_SYNC;
             }
 
@@ -2999,8 +2983,8 @@
     }
 
     @Override
-    public void screenTurningOff(ScreenOffListener listener) {
-        mTaskSnapshotController.screenTurningOff(listener);
+    public void screenTurningOff(int displayId, ScreenOffListener listener) {
+        mTaskSnapshotController.screenTurningOff(displayId, listener);
     }
 
     @Override
@@ -5095,7 +5079,6 @@
 
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_HIDE_TIMEOUT = 52;
-        public static final int SEAMLESS_ROTATION_TIMEOUT = 54;
         public static final int RESTORE_POINTER_ICON = 55;
         public static final int SET_HAS_OVERLAY_UI = 58;
         public static final int ANIMATION_FAILSAFE = 60;
@@ -5378,13 +5361,6 @@
                     }
                     break;
                 }
-                case SEAMLESS_ROTATION_TIMEOUT: {
-                    final DisplayContent displayContent = (DisplayContent) msg.obj;
-                    synchronized (mGlobalLock) {
-                        displayContent.getDisplayRotation().onSeamlessRotationTimeout();
-                    }
-                    break;
-                }
                 case SET_HAS_OVERLAY_UI: {
                     mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
                     break;
@@ -5702,6 +5678,11 @@
         mBlurController.unregisterCrossWindowBlurEnabledListener(listener);
     }
 
+    @Override
+    public void setForceCrossWindowBlurDisabled(boolean disable) {
+        mBlurController.setForceCrossWindowBlurDisabled(disable);
+    }
+
     // -------------------------------------------------------------
     // Internals
     // -------------------------------------------------------------
@@ -8655,14 +8636,16 @@
             final WindowState win = windowForClientLocked(session, window, false);
             if (win == null) {
                 Slog.w(TAG, "Failed to generate DisplayHash. Invalid window");
-                sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW);
+                mDisplayHashController.sendDisplayHashError(callback,
+                        DISPLAY_HASH_ERROR_MISSING_WINDOW);
                 return;
             }
 
             DisplayContent displayContent = win.getDisplayContent();
             if (displayContent == null) {
                 Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
-                sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+                mDisplayHashController.sendDisplayHashError(callback,
+                        DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
                 return;
             }
 
@@ -8672,7 +8655,8 @@
 
             if (boundsInDisplay.isEmpty()) {
                 Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen");
-                sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+                mDisplayHashController.sendDisplayHashError(callback,
+                        DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
                 return;
             }
         }
@@ -8682,23 +8666,13 @@
         // be covering it with the same uid. We want to make sure we include content that's
         // covering to ensure we get as close as possible to what the user sees
         final int uid = session.mUid;
-        SurfaceControl.LayerCaptureArgs args =
+        SurfaceControl.LayerCaptureArgs.Builder args =
                 new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl)
                         .setUid(uid)
-                        .setSourceCrop(boundsInDisplay)
-                        .build();
+                        .setSourceCrop(boundsInDisplay);
 
-        SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
-                SurfaceControl.captureLayers(args);
-        if (screenshotHardwareBuffer == null
-                || screenshotHardwareBuffer.getHardwareBuffer() == null) {
-            Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
-            sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
-            return;
-        }
-
-        mDisplayHashController.generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(),
-                boundsInWindow, hashAlgorithm, callback);
+        mDisplayHashController.generateDisplayHash(args, boundsInWindow,
+                hashAlgorithm, callback);
     }
 
     boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
@@ -8716,10 +8690,4 @@
             return snapshot != null && snapshot.hasImeSurface();
         }
     }
-
-    private void sendDisplayHashError(RemoteCallback callback, int errorCode) {
-        Bundle bundle = new Bundle();
-        bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
-        callback.sendResult(bundle);
-    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d9b879f..2eadcd5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -168,8 +168,8 @@
 import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
 import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE;
+import static com.android.server.wm.WindowStateProto.HAS_COMPAT_SCALE;
 import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
-import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE;
 import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
@@ -261,6 +261,7 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /** A window in the window manager. */
@@ -749,6 +750,36 @@
     private final WindowProcessController mWpcForDisplayAreaConfigChanges;
 
     /**
+     * We split the draw handlers in to a "pending" and "ready" list, in order to solve
+     * sequencing problems. Think of it this way, let's say I update a windows orientation
+     * (in configuration), and then I call applyWithNextDraw. What I'm hoping for is to
+     * apply with the draw that contains the orientation change. However, since the client
+     * can call finishDrawing at any time, it could be about to call a previous call to
+     * finishDrawing (or maybe its already called it, we just haven't handled it). Since this
+     * frame was already completed it had no time to include the orientation change we made.
+     * To solve this problem we accumulate draw handlers in mPendingDrawHandlers, and then force
+     * the client to call relayout. Only the frame post relayout will contain the configuration
+     * change since the window has to relayout), and so in relayout we drain mPendingDrawHandlers
+     * into mReadyDrawHandlers. Finally once we get to finishDrawing we know everything in
+     * mReadyDrawHandlers corresponds to state which was observed by the client and we can
+     * invoke the consumers.
+     */
+    private final List<Consumer<SurfaceControl.Transaction>> mPendingDrawHandlers
+        = new ArrayList<>();
+    private final List<Consumer<SurfaceControl.Transaction>> mReadyDrawHandlers
+        = new ArrayList<>();
+
+    private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
+        finishSeamlessRotation(t);
+        updateSurfacePosition(t);
+    };
+
+    /**
+     * @see #setSurfaceTranslationY(int)
+     */
+    private int mSurfaceTranslationY;
+
+    /**
      * Returns the visibility of the given {@link InternalInsetsType type} requested by the client.
      *
      * @param type the given {@link InternalInsetsType type}.
@@ -831,19 +862,27 @@
             mPendingSeamlessRotate.unrotate(transaction, this);
             getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
                     true /* seamlesslyRotated */);
+            applyWithNextDraw(mSeamlessRotationFinishedConsumer);
         }
     }
 
-    void finishSeamlessRotation(boolean timeout) {
-        if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.finish(this, timeout);
-            mFinishSeamlessRotateFrameNumber = getFrameNumber();
-            mPendingSeamlessRotate = null;
-            getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
-                    false /* seamlesslyRotated */);
-            if (mControllableInsetProvider != null) {
-                mControllableInsetProvider.finishSeamlessRotation(timeout);
-            }
+    void cancelSeamlessRotation() {
+        finishSeamlessRotation(getPendingTransaction());
+    }
+
+    void finishSeamlessRotation(SurfaceControl.Transaction t) {
+        if (mPendingSeamlessRotate == null) {
+            return;
+        }
+
+        mPendingSeamlessRotate.finish(t, this);
+        mFinishSeamlessRotateFrameNumber = getFrameNumber();
+        mPendingSeamlessRotate = null;
+
+        getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
+            false /* seamlesslyRotated */);
+        if (mControllableInsetProvider != null) {
+            mControllableInsetProvider.finishSeamlessRotation();
         }
     }
 
@@ -1050,18 +1089,18 @@
      * scaling override set.
      * @see CompatModePackages#getCompatScale
      * @see android.content.res.CompatibilityInfo#supportsScreen
-     * @see ActivityRecord#inSizeCompatMode()
+     * @see ActivityRecord#hasSizeCompatBounds()
      */
-    boolean inSizeCompatMode() {
-        return mOverrideScale != 1f || inSizeCompatMode(mAttrs, mActivityRecord);
+    boolean hasCompatScale() {
+        return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
     }
 
     /**
      * @return {@code true} if the application runs in size compatibility mode.
      * @see android.content.res.CompatibilityInfo#supportsScreen
-     * @see ActivityRecord#inSizeCompatMode()
+     * @see ActivityRecord#hasSizeCompatBounds()
      */
-    static boolean inSizeCompatMode(WindowManager.LayoutParams attrs, WindowToken windowToken) {
+    static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
         return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
                 || (windowToken != null && windowToken.hasSizeCompatBounds()
                 // Exclude starting window because it is not displayed by the application.
@@ -1266,7 +1305,7 @@
         windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
 
         windowFrames.mCompatFrame.set(windowFrames.mFrame);
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             // Also the scaled frame that we report to the app needs to be
             // adjusted to be in its coordinate space.
             windowFrames.mCompatFrame.scale(mInvGlobalScale);
@@ -1538,7 +1577,7 @@
      */
     InsetsState getCompatInsetsState() {
         InsetsState state = getInsetsState();
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             state = new InsetsState(state, true);
             state.scale(mInvGlobalScale);
         }
@@ -1676,7 +1715,7 @@
     }
 
     void prelayout() {
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             if (mOverrideScale != 1f) {
                 mGlobalScale = mToken.hasSizeCompatBounds()
                         ? mToken.getSizeCompatScale() * mOverrideScale
@@ -3590,7 +3629,7 @@
     void fillClientWindowFrames(ClientWindowFrames outFrames) {
         outFrames.frame.set(mWindowFrames.mCompatFrame);
         outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
-        if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
+        if (mInvGlobalScale != 1.0f && hasCompatScale()) {
             outFrames.displayFrame.scale(mInvGlobalScale);
         }
 
@@ -3990,7 +4029,7 @@
         proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
         proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
         proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
-        proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
+        proto.write(HAS_COMPAT_SCALE, hasCompatScale());
         proto.write(GLOBAL_SCALE, mGlobalScale);
         proto.end(token);
     }
@@ -4092,7 +4131,7 @@
         pw.println(prefix + "mHasSurface=" + mHasSurface
                 + " isReadyForDisplay()=" + isReadyForDisplay()
                 + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
         }
         if (dumpAll) {
@@ -4215,18 +4254,18 @@
         float x, y;
         int w,h;
 
-        final boolean inSizeCompatMode = inSizeCompatMode();
+        final boolean hasCompatScale = hasCompatScale();
         if ((mAttrs.flags & FLAG_SCALED) != 0) {
             if (mAttrs.width < 0) {
                 w = pw;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 w = (int)(mAttrs.width * mGlobalScale + .5f);
             } else {
                 w = mAttrs.width;
             }
             if (mAttrs.height < 0) {
                 h = ph;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 h = (int)(mAttrs.height * mGlobalScale + .5f);
             } else {
                 h = mAttrs.height;
@@ -4234,21 +4273,21 @@
         } else {
             if (mAttrs.width == MATCH_PARENT) {
                 w = pw;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 w = (int)(mRequestedWidth * mGlobalScale + .5f);
             } else {
                 w = mRequestedWidth;
             }
             if (mAttrs.height == MATCH_PARENT) {
                 h = ph;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 h = (int)(mRequestedHeight * mGlobalScale + .5f);
             } else {
                 h = mRequestedHeight;
             }
         }
 
-        if (inSizeCompatMode) {
+        if (hasCompatScale) {
             x = mAttrs.x * mGlobalScale;
             y = mAttrs.y * mGlobalScale;
         } else {
@@ -4276,7 +4315,7 @@
         // We need to make sure we update the CompatFrame as it is used for
         // cropping decisions, etc, on systems where we lack a decor layer.
         windowFrames.mCompatFrame.set(windowFrames.mFrame);
-        if (inSizeCompatMode) {
+        if (hasCompatScale) {
             // See comparable block in computeFrameLw.
             windowFrames.mCompatFrame.scale(mInvGlobalScale);
         }
@@ -4394,7 +4433,7 @@
 
     float translateToWindowX(float x) {
         float winX = x - mWindowFrames.mFrame.left;
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             winX *= mGlobalScale;
         }
         return winX;
@@ -4402,7 +4441,7 @@
 
     float translateToWindowY(float y) {
         float winY = y - mWindowFrames.mFrame.top;
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             winY *= mGlobalScale;
         }
         return winY;
@@ -5324,6 +5363,8 @@
         // Expand for surface insets. See WindowState.expandForSurfaceInsets.
         transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
         outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
+
+        outPoint.y += mSurfaceTranslationY;
     }
 
     /**
@@ -5331,7 +5372,7 @@
      * scaled, the insets also need to be scaled for surface position in global coordinate.
      */
     private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
-        if (!inSizeCompatMode()) {
+        if (!hasCompatScale()) {
             outPos.x = surfaceInsets.left;
             outPos.y = surfaceInsets.top;
             return;
@@ -5684,6 +5725,8 @@
             Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
             mActivityRecord.mRelaunchStartTime = 0;
         }
+
+        executeDrawHandlers(postDrawTransaction);
         if (!onSyncFinishedDrawing()) {
             return mWinAnimator.finishDrawingLocked(postDrawTransaction);
         }
@@ -5698,6 +5741,7 @@
     }
 
     void immediatelyNotifyBlastSync() {
+        prepareDrawHandlers();
         finishDrawing(null);
         mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
         if (!useBLASTSync()) return;
@@ -5777,4 +5821,75 @@
         outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
                 -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
     }
+
+    /**
+     * This method is used to control whether we return the BLAST_SYNC flag
+     * from relayoutWindow calls on this window (triggering the client to redirect
+     * it's next draw in to a transaction). If we have pending draw handlers, we are
+     * looking for the client to sync.
+     *
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    @Override
+    boolean useBLASTSync() {
+        return super.useBLASTSync() || (mPendingDrawHandlers.size() != 0);
+    }
+
+    /**
+     * Apply the transaction with the next window redraw. A full relayout/finishDrawing
+     * cycle must occur before completion. This means if you call the function while
+     * "in relayout", the results may be undefined but at all other times the function
+     * should sort of transparently work like this:
+     *    1. Make changes to WM hierarchy (say change app configuration)
+     *    2. Call apply with next draw.
+     *    3. After finishDrawing, our consumer will be passed the Transaction
+     *    containing the buffer, and we can merge in additional operations.
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
+        mPendingDrawHandlers.add(consumer);
+        requestRedrawForSync();
+
+        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
+            BLAST_TIMEOUT_DURATION);
+    }
+
+    /**
+     * Called from relayout, to indicate the next "finishDrawing" will contain
+     * all changes applied by the time mPendingDrawHandlers was populated.
+     *
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    void prepareDrawHandlers() {
+        mReadyDrawHandlers.addAll(mPendingDrawHandlers);
+        mPendingDrawHandlers.clear();
+    }
+
+    /**
+     * Drain the draw handlers, called from finishDrawing()
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    boolean executeDrawHandlers(SurfaceControl.Transaction t) {
+        if (t == null) t = mTmpTransaction;
+        boolean hadHandlers = false;
+        for (int i = 0; i < mReadyDrawHandlers.size(); i++) {
+            mReadyDrawHandlers.get(i).accept(t);
+            hadHandlers = true;
+        }
+        mReadyDrawHandlers.clear();
+        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+
+        t.apply();
+
+        return hadHandlers;
+    }
+
+    /**
+     * Adds an additional translation offset to be applied when positioning the surface. Used to
+     * correct offsets in specific reparenting situations, e.g. the navigation bar window attached
+     * on the lower split-screen app.
+     */
+    void setSurfaceTranslationY(int translationY) {
+        mSurfaceTranslationY = translationY;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index e8b8bfc..0bb97f5 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -114,15 +114,6 @@
      * @param pw Print writer
      */
     void stopTrace(@Nullable PrintWriter pw) {
-        stopTrace(pw, true /* writeToFile */);
-    }
-
-    /**
-     * Stops the trace
-     * @param pw Print writer
-     * @param writeToFile If the current buffer should be written to disk or not
-     */
-    void stopTrace(@Nullable PrintWriter pw, boolean writeToFile) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
@@ -135,12 +126,35 @@
                 logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
                 throw new IllegalStateException("tracing enabled while waiting for flush.");
             }
-            if (writeToFile) {
-                writeTraceToFileLocked();
-                logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
-            }
+            writeTraceToFileLocked();
+            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
         }
-        ProtoLogImpl.getSingleInstance().stopProtoLog(pw, writeToFile);
+        ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
+    }
+
+    /**
+     * Stops the trace and write the current buffer to disk then restart, if it's already running.
+     * @param pw Print writer
+     */
+    void saveForBugreport(@Nullable PrintWriter pw) {
+        if (IS_USER) {
+            logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
+            return;
+        }
+        synchronized (mEnabledLock) {
+            if (!mEnabled) {
+                return;
+            }
+            mEnabled = mEnabledLockFree = false;
+            logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
+            writeTraceToFileLocked();
+            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+            ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
+            logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+            mBuffer.resetBuffer();
+            mEnabled = mEnabledLockFree = true;
+            ProtoLogImpl.getSingleInstance().startProtoLog(pw);
+        }
     }
 
     private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
@@ -188,6 +202,9 @@
             case "stop":
                 stopTrace(pw);
                 return 0;
+            case "save-for-bugreport":
+                saveForBugreport(pw);
+                return 0;
             case "status":
                 logAndPrintln(pw, getStatus());
                 return 0;
@@ -230,6 +247,7 @@
                 pw.println("Window manager trace options:");
                 pw.println("  start: Start logging");
                 pw.println("  stop: Stop logging");
+                pw.println("  save-for-bugreport: Save logging data to file if it's running.");
                 pw.println("  frame: Log trace once per frame");
                 pw.println("  transaction: Log each transaction");
                 pw.println("  size: Set the maximum log size (in KB)");
@@ -316,19 +334,6 @@
         }
     }
 
-    /**
-     * Writes the trace buffer to new file for the bugreport.
-     *
-     * This method is synchronized with {@code #startTrace(PrintWriter)} and
-     * {@link #stopTrace(PrintWriter)}.
-     */
-    void writeTraceToFile() {
-        synchronized (mEnabledLock) {
-            writeTraceToFileLocked();
-        }
-        ProtoLogImpl.getSingleInstance().writeProtoLogToFile();
-    }
-
     private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
         Log.i(TAG, msg);
         if (pw != null) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 10705af..be06d03 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1783,8 +1783,9 @@
     im->setSystemUiLightsOut(lightsOut);
 }
 
-static jboolean nativeTransferTouchFocus(JNIEnv* env,
-        jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) {
+static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                         jobject fromChannelTokenObj, jobject toChannelTokenObj,
+                                         jboolean isDragDrop) {
     if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
         return JNI_FALSE;
     }
@@ -1793,8 +1794,8 @@
     sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
 
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-    if (im->getInputManager()->getDispatcher()->transferTouchFocus(
-            fromChannelToken, toChannelToken)) {
+    if (im->getInputManager()->getDispatcher()->transferTouchFocus(fromChannelToken, toChannelToken,
+                                                                   isDragDrop)) {
         return JNI_TRUE;
     } else {
         return JNI_FALSE;
@@ -2267,7 +2268,7 @@
          (void*)nativeRequestPointerCapture},
         {"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode},
         {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
-        {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
+        {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z",
          (void*)nativeTransferTouchFocus},
         {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
         {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index ef2d0ba..f60b354 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -32,8 +32,6 @@
 #include "com_android_server_vibrator_VibratorManagerService.h"
 
 namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
 namespace V1_3 = android::hardware::vibrator::V1_3;
 namespace aidl = android::hardware::vibrator;
 
@@ -85,10 +83,11 @@
 class VibratorControllerWrapper {
 public:
     VibratorControllerWrapper(JNIEnv* env, int32_t vibratorId, jobject callbackListener)
-          : mHal(std::move(findVibrator(vibratorId))),
+          : mHal(findVibrator(vibratorId)),
             mVibratorId(vibratorId),
             mCallbackListener(env->NewGlobalRef(callbackListener)) {
-        LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator hal");
+        LOG_ALWAYS_FATAL_IF(mHal == nullptr,
+                            "Failed to connect to vibrator HAL, or vibratorId is invalid");
         LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
                             "Unable to create global reference to vibration callback handler");
     }
@@ -130,15 +129,15 @@
     }
 }
 
-static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jint vibratorId,
-                          jobject callbackListener) {
+static jlong vibratorNativeInit(JNIEnv* env, jclass /* clazz */, jint vibratorId,
+                                jobject callbackListener) {
     std::unique_ptr<VibratorControllerWrapper> wrapper =
             std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener);
     wrapper->hal()->init();
     return reinterpret_cast<jlong>(wrapper.release());
 }
 
-static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+static jlong vibratorGetNativeFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeWrapper));
 }
 
@@ -286,25 +285,46 @@
     wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id));
 }
 
+static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+    VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+    if (wrapper == nullptr) {
+        ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized");
+        return NAN;
+    }
+    auto result = wrapper->hal()->getResonantFrequency();
+    return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+}
+
+static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+    VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+    if (wrapper == nullptr) {
+        ALOGE("vibratorGetQFactor failed because native wrapper was not initialized");
+        return NAN;
+    }
+    auto result = wrapper->hal()->getQFactor();
+    return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+}
+
 static const JNINativeMethod method_table[] = {
-        {"vibratorInit",
+        {"nativeInit",
          "(ILcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;)J",
-         (void*)vibratorInit},
-        {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
-        {"vibratorIsAvailable", "(J)Z", (void*)vibratorIsAvailable},
-        {"vibratorOn", "(JJJ)V", (void*)vibratorOn},
-        {"vibratorOff", "(J)V", (void*)vibratorOff},
-        {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
-        {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
-        {"vibratorPerformComposedEffect",
-         "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
+         (void*)vibratorNativeInit},
+        {"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer},
+        {"isAvailable", "(J)Z", (void*)vibratorIsAvailable},
+        {"on", "(JJJ)V", (void*)vibratorOn},
+        {"off", "(J)V", (void*)vibratorOff},
+        {"setAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
+        {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
+        {"performComposedEffect", "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
          (void*)vibratorPerformComposedEffect},
-        {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
-        {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
-        {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
-        {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
-        {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
-        {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
+        {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+        {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
+        {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
+        {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities},
+        {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
+        {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
+        {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency},
+        {"getQFactor", "(J)F", (void*)vibratorGetQFactor},
 };
 
 int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -320,7 +340,8 @@
     sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
     sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
 
-    return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController",
+    return jniRegisterNativeMethods(env,
+                                    "com/android/server/vibrator/VibratorController$NativeWrapper",
                                     method_table, NELEM(method_table));
 }
 
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 6a8f6d4..d43cf3f 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -45,13 +45,6 @@
 }
 
 xsd_config {
-    name: "cec-config",
-    srcs: ["cec-config/cec-config.xsd"],
-    api_dir: "cec-config/schema",
-    package_name: "com.android.server.hdmi.cec.config",
-}
-
-xsd_config {
     name: "device-state-config",
     srcs: ["device-state-config/device-state-config.xsd"],
     api_dir: "device-state-config/schema",
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
deleted file mode 100644
index b59c93c..0000000
--- a/services/core/xsd/cec-config/cec-config.xsd
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xs:schema version="2.0"
-           elementFormDefault="qualified"
-           attributeFormDefault="unqualified"
-  xmlns:xs="http://www.w3.org/2001/XMLSchema">
-  <xs:element name="cec-settings">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/>
-      </xs:sequence>
-    </xs:complexType>
-  </xs:element>
-  <xs:complexType name="setting">
-    <xs:attribute name="name" type="xs:string"/>
-    <xs:attribute name="value-type" type="xs:string"/>
-    <xs:attribute name="user-configurable" type="xs:boolean"/>
-    <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
-    <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
-  </xs:complexType>
-  <xs:complexType name="value-list">
-      <xs:sequence>
-        <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/>
-      </xs:sequence>
-  </xs:complexType>
-  <xs:complexType name="value">
-    <xs:attribute name="string-value" type="xs:string"/>
-    <xs:attribute name="int-value" type="xs:string"/>
-  </xs:complexType>
-</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
deleted file mode 100644
index 75872d4..0000000
--- a/services/core/xsd/cec-config/schema/current.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-// Signature format: 2.0
-package com.android.server.hdmi.cec.config {
-
-  public class CecSettings {
-    ctor public CecSettings();
-    method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting();
-  }
-
-  public class Setting {
-    ctor public Setting();
-    method public com.android.server.hdmi.cec.config.ValueList getAllowedValues();
-    method public com.android.server.hdmi.cec.config.Value getDefaultValue();
-    method public String getName();
-    method public boolean getUserConfigurable();
-    method public String getValueType();
-    method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
-    method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
-    method public void setName(String);
-    method public void setUserConfigurable(boolean);
-    method public void setValueType(String);
-  }
-
-  public class Value {
-    ctor public Value();
-    method public String getIntValue();
-    method public String getStringValue();
-    method public void setIntValue(String);
-    method public void setStringValue(String);
-  }
-
-  public class ValueList {
-    ctor public ValueList();
-    method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue();
-  }
-
-  public class XmlParser {
-    ctor public XmlParser();
-    method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-  }
-
-}
-
diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt
deleted file mode 100644
index e69de29..0000000
--- a/services/core/xsd/cec-config/schema/last_current.txt
+++ /dev/null
diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt
deleted file mode 100644
index e69de29..0000000
--- a/services/core/xsd/cec-config/schema/last_removed.txt
+++ /dev/null
diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/services/core/xsd/cec-config/schema/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48ae8d6..aed13b2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -19,6 +19,8 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -38,7 +40,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
-import android.util.Log;
 import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -455,8 +456,7 @@
                     try {
                         trustAgentInfo.options.saveToXml(out);
                     } catch (XmlPullParserException e) {
-                        Log.e(DevicePolicyManagerService.LOG_TAG,
-                                "Failed to save TrustAgent options", e);
+                        Slog.e(LOG_TAG, e, "Failed to save TrustAgent options");
                     }
                     out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
                 }
@@ -629,8 +629,7 @@
             String tag = parser.getName();
             if (TAG_POLICIES.equals(tag)) {
                 if (shouldOverridePolicies) {
-                    Log.d(DevicePolicyManagerService.LOG_TAG,
-                            "Overriding device admin policies from XML.");
+                    Slog.d(LOG_TAG, "Overriding device admin policies from XML.");
                     info.readPoliciesFromXml(parser);
                 }
             } else if (TAG_PASSWORD_QUALITY.equals(tag)) {
@@ -726,16 +725,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     shortSupportMessage = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing text when loading short support message");
+                    Slog.w(LOG_TAG, "Missing text when loading short support message");
                 }
             } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     longSupportMessage = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing text when loading long support message");
+                    Slog.w(LOG_TAG, "Missing text when loading long support message");
                 }
             } else if (TAG_PARENT_ADMIN.equals(tag)) {
                 Preconditions.checkState(!isParent);
@@ -748,8 +745,7 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     organizationName = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing text when loading organization name");
+                    Slog.w(LOG_TAG, "Missing text when loading organization name");
                 }
             } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
                 isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
@@ -758,16 +754,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     startUserSessionMessage = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing text when loading start session message");
+                    Slog.w(LOG_TAG, "Missing text when loading start session message");
                 }
             } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     endUserSessionMessage = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing text when loading end session message");
+                    Slog.w(LOG_TAG, "Missing text when loading end session message");
                 }
             } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
                 mCrossProfileCalendarPackages = readPackageList(parser, tag);
@@ -802,16 +796,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     mOrganizationId = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing Organization ID.");
+                    Slog.w(LOG_TAG, "Missing Organization ID.");
                 }
             } else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     mEnrollmentSpecificId = parser.getText();
                 } else {
-                    Log.w(DevicePolicyManagerService.LOG_TAG,
-                            "Missing Enrollment-specific ID.");
+                    Slog.w(LOG_TAG, "Missing Enrollment-specific ID.");
                 }
             } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
                 mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
@@ -820,7 +812,7 @@
                 mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
                         USB_DATA_SIGNALING_ENABLED_DEFAULT);
             } else {
-                Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
+                Slog.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
             }
         }
@@ -842,12 +834,10 @@
                 if (packageName != null) {
                     result.add(packageName);
                 } else {
-                    Slog.w(DevicePolicyManagerService.LOG_TAG,
-                            "Package name missing under " + outerTag);
+                    Slog.w(LOG_TAG, "Package name missing under %s", outerTag);
                 }
             } else {
-                Slog.w(DevicePolicyManagerService.LOG_TAG,
-                        "Unknown tag under " + tag +  ": " + outerTag);
+                Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
             }
         }
         return result;
@@ -868,8 +858,7 @@
             if (tag.equals(tagDAM)) {
                 result.add(parser.getAttributeValue(null, ATTR_VALUE));
             } else {
-                Slog.e(DevicePolicyManagerService.LOG_TAG,
-                        "Expected tag " + tag +  " but found " + tagDAM);
+                Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
             }
         }
     }
@@ -891,8 +880,7 @@
                 final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
                 result.put(component, trustAgentInfo);
             } else {
-                Slog.w(DevicePolicyManagerService.LOG_TAG,
-                        "Unknown tag under " + tag +  ": " + tagDAM);
+                Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
             }
         }
         return result;
@@ -912,8 +900,7 @@
             if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
                 result.options = PersistableBundle.restoreFromXml(parser);
             } else {
-                Slog.w(DevicePolicyManagerService.LOG_TAG,
-                        "Unknown tag under " + tag +  ": " + tagDAM);
+                Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
             }
         }
         return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index d812b8f..e0c5e32 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -16,6 +16,8 @@
 
 package com.android.server.devicepolicy;
 
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -33,7 +35,7 @@
 import android.security.Credentials;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -47,7 +49,6 @@
 import java.util.List;
 
 public class CertificateMonitor {
-    protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
     protected static final int MONITORING_CERT_NOTIFICATION_ID = SystemMessage.NOTE_SSL_CERT_INFO;
 
     private final DevicePolicyManagerService mService;
@@ -78,16 +79,16 @@
             X509Certificate cert = parseCert(certBuffer);
             pemCert = Credentials.convertToPem(cert);
         } catch (CertificateException | IOException ce) {
-            Log.e(LOG_TAG, "Problem converting cert", ce);
+            Slog.e(LOG_TAG, ce, "Problem converting cert");
             return null;
         }
 
         try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
             return keyChainConnection.getService().installCaCertificate(pemCert);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+            Slog.e(LOG_TAG, e, "installCaCertsToKeyChain(): ");
         } catch (InterruptedException e1) {
-            Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+            Slog.w(LOG_TAG, e1, "installCaCertsToKeyChain(): ");
             Thread.currentThread().interrupt();
         }
         return null;
@@ -99,9 +100,9 @@
                 keyChainConnection.getService().deleteCaCertificate(aliases[i]);
             }
         } catch (RemoteException e) {
-            Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
+            Slog.e(LOG_TAG, e, "from CaCertUninstaller: ");
         } catch (InterruptedException ie) {
-            Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+            Slog.w(LOG_TAG, ie, "CaCertUninstaller: ");
             Thread.currentThread().interrupt();
         }
     }
@@ -137,7 +138,8 @@
     };
 
     private void updateInstalledCertificates(final UserHandle userHandle) {
-        if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+        final int userId = userHandle.getIdentifier();
+        if (!mInjector.getUserManager().isUserUnlocked(userId)) {
             return;
         }
 
@@ -145,7 +147,8 @@
         try {
             installedCerts = getInstalledCaCertificates(userHandle);
         } catch (RemoteException | RuntimeException e) {
-            Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+            Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
+                    userId);
             return;
         }
         mService.onInstalledCertificatesChanged(userHandle, installedCerts);
@@ -167,7 +170,7 @@
         try {
             userContext = mInjector.createContextAsUser(userHandle);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+            Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle);
             return null;
         }
 
@@ -183,7 +186,6 @@
             smallIconId = R.drawable.stat_sys_certificate_info;
             parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
         } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
-            final String ownerName = mService.getDeviceOwnerName();
             contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
                     mService.getDeviceOwnerName());
             smallIconId = R.drawable.stat_sys_certificate_info;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 3067d45..00e0292 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -46,19 +46,11 @@
     final Object mLock = new Object();
     final Context mContext;
 
-    private final DevicePolicyManagerService mService;
     private final DevicePolicyManagerService.Injector mInjector;
     private final DevicePolicyConstants mConstants;
 
     private final Handler mHandler; // needed?
 
-    static void debug(String format, Object... args) {
-        if (!DEBUG) {
-            return;
-        }
-        Slog.d(TAG, String.format(format, args));
-    }
-
     private class DevicePolicyServiceConnection
             extends PersistentConnection<IDeviceAdminService> {
         public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
@@ -88,7 +80,6 @@
 
     public DeviceAdminServiceController(DevicePolicyManagerService service,
             DevicePolicyConstants constants) {
-        mService = service;
         mInjector = service.mInjector;
         mContext = mInjector.mContext;
         mHandler = new Handler(BackgroundThread.get().getLooper());
@@ -122,8 +113,9 @@
             synchronized (mLock) {
                 final ServiceInfo service = findService(packageName, userId);
                 if (service == null) {
-                    debug("Owner package %s on u%d has no service.",
-                            packageName, userId);
+                    if (DEBUG) {
+                        Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId);
+                    }
                     disconnectServiceOnUserLocked(userId, actionForLog);
                     return;
                 }
@@ -134,14 +126,17 @@
                     // Note even when we're already connected to the same service, the binding
                     // would have died at this point due to a package update.  So we disconnect
                     // anyway and re-connect.
-                    debug("Disconnecting from existing service connection.",
-                            packageName, userId);
+                    if (DEBUG) {
+                        Slog.d("Disconnecting from existing service connection.", packageName,
+                                userId);
+                    }
                     disconnectServiceOnUserLocked(userId, actionForLog);
                 }
 
-                debug("Owner package %s on u%d has service %s for %s",
-                        packageName, userId,
+                if (DEBUG) {
+                    Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId,
                         service.getComponentName().flattenToShortString(), actionForLog);
+                }
 
                 final DevicePolicyServiceConnection conn =
                         new DevicePolicyServiceConnection(
@@ -172,8 +167,10 @@
     private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
         final DevicePolicyServiceConnection conn = mConnections.get(userId);
         if (conn != null) {
-            debug("Stopping service for u%d if already running for %s.",
-                    userId, actionForLog);
+            if (DEBUG) {
+                Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId,
+                        actionForLog);
+            }
             conn.unbind();
             mConnections.remove(userId);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 464d6f5..84e6da0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -88,7 +88,7 @@
         } catch (IllegalArgumentException e) {
             // Failed to parse the settings string, log this and move on
             // with defaults.
-            Slog.e(TAG, "Bad device policy settings: " + settings);
+            Slog.e(TAG, "Bad device policy settings: %s", settings);
         }
 
         long dasDiedServiceReconnectBackoffSec = parser.getLong(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index c0b2ed4..52cdce6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -179,11 +179,11 @@
      */
     static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) {
         FileOutputStream stream = null;
+        File chooseForWrite = null;
         try {
-            File chooseForWrite = file.chooseForWrite();
+            chooseForWrite = file.chooseForWrite();
             if (VERBOSE_LOG) {
-                Slog.v(TAG, "Storing data for user " + policyData.mUserId + " on "
-                        + chooseForWrite);
+                Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
             }
             stream = new FileOutputStream(chooseForWrite, false);
             TypedXmlSerializer out = Xml.resolveSerializer(stream);
@@ -195,7 +195,7 @@
                         policyData.mRestrictionsProvider.flattenToString());
             }
             if (policyData.mUserSetupComplete) {
-                if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true");
+                if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
                 out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
             }
             if (policyData.mPaired) {
@@ -216,8 +216,8 @@
 
             if (policyData.mFactoryResetFlags != 0) {
                 if (VERBOSE_LOG) {
-                    Slog.v(TAG, "Storing factory reset flags for user " + policyData.mUserId + ": "
-                            + factoryResetFlagsToString(policyData.mFactoryResetFlags));
+                    Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
+                            factoryResetFlagsToString(policyData.mFactoryResetFlags));
                 }
                 out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
             }
@@ -382,7 +382,7 @@
             file.commit();
             return true;
         } catch (XmlPullParserException | IOException e) {
-            Slog.w(TAG, "failed writing file", e);
+            Slog.w(TAG, e, "failed writing file %s", chooseForWrite);
             try {
                 if (stream != null) {
                     stream.close();
@@ -404,10 +404,8 @@
             ComponentName ownerComponent) {
         FileInputStream stream = null;
         File file = journaledFile.chooseForRead();
-        if (VERBOSE_LOG) {
-            Slog.v(TAG, "Loading data for user " + policy.mUserId + " from " + file);
-        }
-
+        if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
+        boolean needsRewrite = false;
         try {
             stream = new FileInputStream(file);
             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -454,8 +452,8 @@
 
             policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
             if (VERBOSE_LOG) {
-                Slog.v(TAG, "Restored factory reset flags for user " + policy.mUserId + ": "
-                        + factoryResetFlagsToString(policy.mFactoryResetFlags));
+                Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
+                        factoryResetFlagsToString(policy.mFactoryResetFlags));
             }
             policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
 
@@ -488,7 +486,7 @@
                             policy.mAdminMap.put(ap.info.getComponent(), ap);
                         }
                     } catch (RuntimeException e) {
-                        Slog.w(TAG, "Failed loading admin " + name, e);
+                        Slog.w(TAG, e, "Failed loading admin %s", name);
                     }
                 } else if ("delegation".equals(tag)) {
                     // Parse delegation info.
@@ -560,7 +558,7 @@
                     policy.mAppsSuspended =
                             parser.getAttributeBoolean(null, ATTR_VALUE, false);
                 } else {
-                    Slog.w(TAG, "Unknown tag: " + tag);
+                    Slog.w(TAG, "Unknown tag: %s", tag);
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
@@ -568,7 +566,7 @@
             // Don't be noisy, this is normal if we haven't defined any policies.
         } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
                 | IndexOutOfBoundsException e) {
-            Slog.w(TAG, "failed parsing " + file, e);
+            Slog.w(TAG, e, "failed parsing %s", file);
         }
         try {
             if (stream != null) {
@@ -592,8 +590,8 @@
                 }
             }
             if (!haveOwner) {
-                Slog.w(TAG, "Previous password owner " + mPasswordOwner
-                        + " no longer active; disabling");
+                Slog.w(TAG, "Previous password owner %s no longer active; disabling",
+                        mPasswordOwner);
                 mPasswordOwner = -1;
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a3dadd8..ac2281a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -473,7 +473,7 @@
     // to decide whether an existing policy in the {@link #DEVICE_POLICIES_XML} needs to
     // be upgraded. See {@link PolicyVersionUpgrader} on instructions how to add an upgrade
     // step.
-    static final int DPMS_VERSION = 1;
+    static final int DPMS_VERSION = 2;
 
     static {
         SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
@@ -1106,7 +1106,7 @@
      * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
      */
     void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
-        Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker));
+        Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
         mSafetyChecker = safetyChecker;
         mInjector.setDevicePolicySafetyChecker(safetyChecker);
     }
@@ -1588,7 +1588,7 @@
             CryptoTestHelper.runAndLogSelfTest();
         }
 
-        public String[] getPersonalAppsForSuspension(int userId) {
+        public String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
             return PersonalAppsSuspensionHelper.forUser(mContext, userId)
                     .getPersonalAppsForSuspension();
         }
@@ -2969,8 +2969,9 @@
 
     private class DpmsUpgradeDataProvider implements PolicyUpgraderDataProvider {
         @Override
-        public boolean isUserDeviceOwner(int userId) {
-            return mOwners.isDeviceOwnerUserId(userId);
+        public boolean isDeviceOwner(int userId, ComponentName who) {
+            return mOwners.isDeviceOwnerUserId(userId)
+                    && mOwners.getDeviceOwnerComponent().equals(who);
         }
 
         @Override
@@ -2998,14 +2999,19 @@
             return component -> findAdmin(component, userId, /* throwForMissingPermission= */
                     false);
         }
+
+        @Override
+        public int[] getUsersForUpgrade() {
+            List<UserInfo> allUsers = mUserManager.getUsers();
+            return allUsers.stream().mapToInt(u -> u.id).toArray();
+        }
     }
 
     private void performPolicyVersionUpgrade() {
-        List<UserInfo> allUsers = mUserManager.getUsers();
         PolicyVersionUpgrader upgrader = new PolicyVersionUpgrader(
                 new DpmsUpgradeDataProvider());
 
-        upgrader.upgradePolicy(allUsers.stream().mapToInt(u -> u.id).toArray(), DPMS_VERSION);
+        upgrader.upgradePolicy(DPMS_VERSION);
     }
 
     private void revertTransferOwnershipIfNecessaryLocked() {
@@ -8811,6 +8817,9 @@
                 return null;
             }
             final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor);
+            if (supervisorComponent == null) {
+                return null;
+            }
             if (supervisorComponent.equals(doComponent) || supervisorComponent.equals(
                     poComponent)) {
                 return supervisorComponent;
@@ -9329,6 +9338,8 @@
         dumpResources(pw, mContext, "vendor_cross_profile_apps", R.array.vendor_cross_profile_apps);
         dumpResources(pw, mContext, "config_packagesExemptFromSuspension",
                 R.array.config_packagesExemptFromSuspension);
+        dumpResources(pw, mContext, "policy_exempt_apps", R.array.policy_exempt_apps);
+        dumpResources(pw, mContext, "vendor_policy_exempt_apps", R.array.vendor_policy_exempt_apps);
         pw.decreaseIndent();
         pw.println();
     }
@@ -10612,6 +10623,30 @@
     }
 
     @Override
+    public List<String> listPolicyExemptApps() {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+        // TODO(b/181238156): decide whether it should only list the apps set by the resources,
+        // or also the "critical" apps defined by PersonalAppsSuspensionHelper (like SMS app).
+        // If it's the latter, refactor PersonalAppsSuspensionHelper so it (or a superclass) takes
+        // the resources on constructor.
+        String[] core = mContext.getResources().getStringArray(R.array.policy_exempt_apps);
+        String[] vendor = mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps);
+
+        int size = core.length + vendor.length;
+        Set<String> apps = new ArraySet<>(size);
+        for (String app : core) {
+            apps.add(app);
+        }
+        for (String app : vendor) {
+            apps.add(app);
+        }
+
+        return new ArrayList<>(apps);
+    }
+
+    @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
             boolean parent) {
         Objects.requireNonNull(who, "ComponentName is null");
@@ -14214,10 +14249,6 @@
                 hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
         enforceCallerSystemUserHandle();
 
-        // no effect if it's called from user build
-        if (!mInjector.isBuildDebuggable()) {
-            return;
-        }
         final int userId = UserHandle.USER_SYSTEM;
         boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
                 Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
@@ -15448,10 +15479,8 @@
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
 
-        String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
-        if (currentMode == null) {
-            currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
-        }
+        final String currentMode =
+                ConnectivityManager.getPrivateDnsMode(mContext.getContentResolver());
         switch (currentMode) {
             case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
                 return PRIVATE_DNS_MODE_OFF;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 5484a14..8e31029 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -21,6 +21,7 @@
 import com.android.server.devicepolicy.Owners.OwnerDto;
 
 import java.io.PrintWriter;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 
@@ -30,6 +31,7 @@
     private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
     private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
     private static final String CMD_LIST_OWNERS = "list-owners";
+    private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
 
     private final DevicePolicyManagerService mService;
 
@@ -60,6 +62,8 @@
                     return runSetSafeOperation(pw);
                 case CMD_LIST_OWNERS:
                     return runListOwners(pw);
+                case CMD_LIST_POLICY_EXEMPT_APPS:
+                    return runListPolicyExemptApps(pw);
                 default:
                     return onInvalidCommand(pw, cmd);
             }
@@ -88,6 +92,8 @@
                 + " \n\n");
         pw.printf("  %s\n", CMD_LIST_OWNERS);
         pw.printf("    Lists the device / profile owners per user \n\n");
+        pw.printf("  %s\n", CMD_LIST_POLICY_EXEMPT_APPS);
+        pw.printf("    Lists the apps that are exempt from policies\n\n");
     }
 
     private int runIsSafeOperation(PrintWriter pw) {
@@ -119,18 +125,20 @@
         return 0;
     }
 
-    private int runListOwners(PrintWriter pw) {
-        List<OwnerDto> owners = mService.listAllOwners();
-        if (owners.isEmpty()) {
-            pw.println("none");
+    private int printAndGetSize(PrintWriter pw, Collection<?> collection, String nameOnSingular) {
+        if (collection.isEmpty()) {
+            pw.printf("no %ss\n", nameOnSingular);
             return 0;
         }
-        int size = owners.size();
-        if (size == 1) {
-            pw.println("1 owner:");
-        } else {
-            pw.printf("%d owners:\n", size);
-        }
+        int size = collection.size();
+        pw.printf("%d %s%s:\n", size, nameOnSingular, (size == 1 ? "" : "s"));
+        return size;
+    }
+
+    private int runListOwners(PrintWriter pw) {
+        List<OwnerDto> owners = mService.listAllOwners();
+        int size = printAndGetSize(pw, owners, "owner");
+        if (size == 0) return 0;
 
         for (int i = 0; i < size; i++) {
             OwnerDto owner = owners.get(i);
@@ -150,4 +158,17 @@
         return 0;
     }
 
+
+    private int runListPolicyExemptApps(PrintWriter pw) {
+        List<String> apps = mService.listPolicyExemptApps();
+        int size = printAndGetSize(pw, apps, "policy exempt app");
+
+        if (size == 0) return 0;
+
+        for (int i = 0; i < size; i++) {
+            String app = apps.get(i);
+            pw.printf("  %d: %s\n", i, app);
+        }
+        return 0;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 457255b..28a6987 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -68,17 +68,16 @@
         IResultReceiver receiver = new IResultReceiver.Stub() {
             @Override
             public void send(int resultCode, Bundle resultData) throws RemoteException {
-                Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding",
-                        mSafetyChecker));
+                Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
                 try {
                     factoryResetInternalUnchecked();
                 } catch (IOException e) {
                     // Shouldn't happen
-                    Slog.wtf(TAG, "IOException calling underlying systems", e);
+                    Slog.wtf(TAG, e, "IOException calling underlying systems");
                 }
             }
         };
-        Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker));
+        Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
         mSafetyChecker.onFactoryReset(receiver);
         return false;
     }
@@ -113,9 +112,9 @@
     }
 
     private void factoryResetInternalUnchecked() throws IOException {
-        Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+        Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
                 + "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
-                mWipeAdoptableStorage, mWipeFactoryResetProtection));
+                mWipeAdoptableStorage, mWipeFactoryResetProtection);
 
         UserManager um = mContext.getSystemService(UserManager.class);
         if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 37dbfc1..0b9ece4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -18,6 +18,8 @@
 
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -36,6 +38,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
+import android.util.Log;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
@@ -105,7 +108,9 @@
             result.remove(pkg);
         }
 
-        Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
+        if (Log.isLoggable(LOG_TAG, Log.INFO)) {
+            Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
+        }
         return result.toArray(new String[0]);
     }
 
@@ -118,7 +123,7 @@
         for (final ResolveInfo resolveInfo : matchingActivities) {
             if (resolveInfo.activityInfo == null
                     || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
-                Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo);
+                Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
                 continue;
             }
             final String packageName = resolveInfo.activityInfo.packageName;
@@ -129,7 +134,8 @@
                     result.add(packageName);
                 }
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName);
+                Slog.e(LOG_TAG, "Could not find application info for launcher app: %s",
+                        packageName);
             }
         }
         return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
index b6420f8..19a7659 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
@@ -33,7 +33,7 @@
      * Returns true if the provided {@code userId} is a device owner. May affect some policy
      * defaults.
      */
-    boolean isUserDeviceOwner(int userId);
+    boolean isDeviceOwner(int userId, ComponentName who);
 
     /**
      * Returns true if the storage manager indicates file-based encryption is enabled.
@@ -60,4 +60,9 @@
      * user.
      */
     Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId);
+
+    /**
+     * Returns the users to upgrade.
+     */
+    int[] getUsersForUpgrade();
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
index cea0863..6bc7ba6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
@@ -62,7 +62,7 @@
      *                 managed profile user IDs.
      * @param dpmsVersion The version to upgrade to.
      */
-    public void upgradePolicy(int[] allUsers, int dpmsVersion) {
+    public void upgradePolicy(int dpmsVersion) {
         int oldVersion = readVersion();
         if (oldVersion >= dpmsVersion) {
             Slog.i(LOG_TAG, String.format("Current version %d, latest version %d, not upgrading.",
@@ -70,6 +70,8 @@
             return;
         }
 
+        final int[] allUsers = mProvider.getUsersForUpgrade();
+
         //NOTE: The current version is provided in case the XML file format changes in a
         // non-backwards-compatible way, so that DeviceAdminData could load it with
         // old tags, for example.
@@ -83,6 +85,27 @@
             currentVersion = 1;
         }
 
+        if (currentVersion == 1) {
+            Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
+            // This upgrade step is for Device Owner scenario only: For devices upgrading to S,
+            // if there is a device owner, it retains the ability to control sensors-related
+            // permission grants.
+            for (int userId : allUsers) {
+                DevicePolicyData userData = allUsersData.get(userId);
+                if (userData == null) {
+                    continue;
+                }
+                for (ActiveAdmin admin : userData.mAdminList) {
+                    if (mProvider.isDeviceOwner(userId, admin.info.getComponent())) {
+                        Slog.i(LOG_TAG, String.format(
+                                "Marking Device Owner in user %d for permission grant ", userId));
+                        admin.mAdminCanGrantSensorsPermissions = true;
+                    }
+                }
+            }
+            currentVersion = 2;
+        }
+
         writePoliciesAndVersion(allUsers, allUsersData, currentVersion);
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 543f381..2959c10 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -25,6 +25,8 @@
 import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
 import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
 
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
 import android.annotation.IntDef;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -58,7 +60,6 @@
  * Class managing bugreport collection upon device owner's request.
  */
 public class RemoteBugreportManager {
-    private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
 
     static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
 
@@ -206,7 +207,7 @@
             return true;
         } catch (RemoteException re) {
             // should never happen
-            Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+            Slog.e(LOG_TAG, re, "Failed to make remote calls to start bugreportremote service");
             return false;
         } finally {
             mInjector.binderRestoreCallingIdentity(callingIdentity);
@@ -220,7 +221,7 @@
             mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
         } catch (IntentFilter.MalformedMimeTypeException e) {
             // should never happen, as setting a constant
-            Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e);
+            Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
         }
         final IntentFilter filterConsent = new IntentFilter();
         filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 5ffbd77..5140b9f 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -68,6 +68,7 @@
         "libutils",
         "libvold_binder",
         "libc++fs",
+        "libziparchive_for_incfs",
     ],
     shared_libs: [
         "libandroidfw",
@@ -77,7 +78,6 @@
         "libincfs",
         "liblog",
         "libz",
-        "libziparchive",
     ],
 }
 
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 1fcc284..c38d0b3 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -74,6 +74,13 @@
 
     // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
     static constexpr auto healthyDataLoaderUptime = 10min;
+
+    // For healthy DLs, we'll retry every ~5secs for ~10min
+    static constexpr auto bindRetryInterval = 5s;
+    static constexpr auto bindGracePeriod = 10min;
+
+    static constexpr auto bindingTimeout = 1min;
+
     // 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
     static constexpr auto minBindDelay = 10s;
     static constexpr auto maxBindDelay = 10000s;
@@ -293,6 +300,7 @@
         mTimedQueue(sm.getTimedQueue()),
         mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
         mFs(sm.getFs()),
+        mClock(sm.getClock()),
         mIncrementalDir(rootDir) {
     CHECK(mVold) << "Vold service is unavailable";
     CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable";
@@ -302,6 +310,7 @@
     CHECK(mTimedQueue) << "TimedQueue is unavailable";
     CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
     CHECK(mFs) << "Fs is unavailable";
+    CHECK(mClock) << "Clock is unavailable";
 
     mJobQueue.reserve(16);
     mJobProcessor = std::thread([this]() {
@@ -2241,17 +2250,44 @@
                << status << " (current " << mCurrentStatus << ")";
 }
 
-Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
+std::optional<Milliseconds> IncrementalService::DataLoaderStub::needToBind() {
     std::unique_lock lock(mMutex);
+
+    const auto now = mService.mClock->now();
+    const bool healthy = (mPreviousBindDelay == 0ms);
+
+    if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_BINDING &&
+        now - mCurrentStatusTs <= Constants::bindingTimeout) {
+        LOG(INFO) << "Binding still in progress. "
+                  << (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times."
+                              : "Already unhealthy, don't do anything.");
+        // Binding still in progress.
+        if (!healthy) {
+            // Already unhealthy, don't do anything.
+            return {};
+        }
+        // The DL is healthy/freshly bound, ok to retry for a few times.
+        if (now - mPreviousBindTs <= Constants::bindGracePeriod) {
+            // Still within grace period.
+            if (now - mCurrentStatusTs >= Constants::bindRetryInterval) {
+                // Retry interval passed, retrying.
+                mCurrentStatusTs = now;
+                mPreviousBindDelay = 0ms;
+                return 0ms;
+            }
+            return {};
+        }
+        // fallthrough, mark as unhealthy, and retry with delay
+    }
+
     const auto previousBindTs = mPreviousBindTs;
-    const auto now = Clock::now();
     mPreviousBindTs = now;
 
     const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
     if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
         nonCrashingInterval > Constants::healthyDataLoaderUptime) {
         mPreviousBindDelay = 0ms;
-        return mPreviousBindDelay;
+        return 0ms;
     }
 
     constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
@@ -2264,12 +2300,16 @@
     const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
     const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
     mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
-
     return mPreviousBindDelay;
 }
 
 bool IncrementalService::DataLoaderStub::bind() {
-    const auto bindDelay = updateBindDelay();
+    const auto maybeBindDelay = needToBind();
+    if (!maybeBindDelay) {
+        LOG(DEBUG) << "Skipping bind to " << mParams.packageName << " because of pending bind.";
+        return true;
+    }
+    const auto bindDelay = *maybeBindDelay;
     if (bindDelay > 1s) {
         LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
                   << bindDelay.count() / 1000 << "s";
@@ -2279,7 +2319,21 @@
     auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
                                                                 this, &result);
     if (!status.isOk() || !result) {
-        LOG(ERROR) << "Failed to bind a data loader for mount " << id();
+        const bool healthy = (bindDelay == 0ms);
+        LOG(ERROR) << "Failed to bind a data loader for mount " << id()
+                   << (healthy ? ", retrying." : "");
+
+        // Internal error, retry for healthy/new DLs.
+        // Let needToBind migrate it to unhealthy after too many retries.
+        if (healthy) {
+            if (mService.addTimedJob(*mService.mTimedQueue, id(), Constants::bindRetryInterval,
+                                     [this]() { fsmStep(); })) {
+                // Mark as binding so that we know it's not the DL's fault.
+                setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_BINDING);
+                return true;
+            }
+        }
+
         return false;
     }
     return true;
@@ -2339,7 +2393,14 @@
             // Do nothing, this is a reset state.
             break;
         case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
-            return destroy();
+            switch (currentStatus) {
+                case IDataLoaderStatusListener::DATA_LOADER_BINDING:
+                    setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+                    return true;
+                default:
+                    return destroy();
+            }
+            break;
         }
         case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
             switch (currentStatus) {
@@ -2353,6 +2414,7 @@
             switch (currentStatus) {
                 case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
                 case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+                case IDataLoaderStatusListener::DATA_LOADER_BINDING:
                     return bind();
                 case IDataLoaderStatusListener::DATA_LOADER_BOUND:
                     return create();
@@ -2372,7 +2434,8 @@
                 fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub");
     }
     if (id() != mountId) {
-        LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+        LOG(ERROR) << "onStatusChanged: mount ID mismatch: expected " << id()
+                   << ", but got: " << mountId;
         return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
     }
     if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
@@ -2396,11 +2459,13 @@
         }
 
         oldStatus = mCurrentStatus;
-        mCurrentStatus = newStatus;
         targetStatus = mTargetStatus;
-
         listener = mStatusListener;
 
+        // Change the status.
+        mCurrentStatus = newStatus;
+        mCurrentStatusTs = mService.mClock->now();
+
         if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
             mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
             // For unavailable, unbind from DataLoader to ensure proper re-commit.
@@ -2428,7 +2493,8 @@
                                          "reportStreamHealth came to invalid DataLoaderStub");
     }
     if (id() != mountId) {
-        LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+        LOG(ERROR) << "reportStreamHealth: mount ID mismatch: expected " << id()
+                   << ", but got: " << mountId;
         return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
     }
     {
@@ -2694,6 +2760,8 @@
 void IncrementalService::DataLoaderStub::onDump(int fd) {
     dprintf(fd, "    dataLoader: {\n");
     dprintf(fd, "      currentStatus: %d\n", mCurrentStatus);
+    dprintf(fd, "      currentStatusTs: %lldmcs\n",
+            (long long)(elapsedMcs(mCurrentStatusTs, Clock::now())));
     dprintf(fd, "      targetStatus: %d\n", mTargetStatus);
     dprintf(fd, "      targetStatusTs: %lldmcs\n",
             (long long)(elapsedMcs(mTargetStatusTs, Clock::now())));
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 14e5a77..4eb5138 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -267,7 +267,10 @@
         BootClockTsUs getOldestTsFromLastPendingReads();
         Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs);
 
-        Milliseconds updateBindDelay();
+        // If the stub has to bind to the DL.
+        // Returns {} if bind operation is already in progress.
+        // Or bind delay in ms.
+        std::optional<Milliseconds> needToBind();
 
         void registerForPendingReads();
         void unregisterFromPendingReads();
@@ -283,6 +286,7 @@
 
         std::condition_variable mStatusCondition;
         int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
+        TimePoint mCurrentStatusTs = {};
         int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
         TimePoint mTargetStatusTs = {};
 
@@ -443,6 +447,7 @@
     const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
     const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
     const std::unique_ptr<FsWrapper> mFs;
+    const std::unique_ptr<ClockWrapper> mClock;
     const std::string mIncrementalDir;
 
     mutable std::mutex mLock;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index d613289..80f409f 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -329,6 +329,14 @@
     }
 };
 
+class RealClockWrapper final : public ClockWrapper {
+public:
+    RealClockWrapper() = default;
+    ~RealClockWrapper() = default;
+
+    TimePoint now() const final { return Clock::now(); }
+};
+
 RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env)
       : mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {}
 
@@ -388,6 +396,10 @@
     return std::make_unique<RealFsWrapper>();
 }
 
+std::unique_ptr<ClockWrapper> RealServiceManager::getClock() {
+    return std::make_unique<RealClockWrapper>();
+}
+
 static JavaVM* getJavaVm(JNIEnv* env) {
     CHECK(env);
     JavaVM* jvm = nullptr;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 245bb31..d113f99 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -158,6 +158,12 @@
     virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0;
 };
 
+class ClockWrapper {
+public:
+    virtual ~ClockWrapper() = default;
+    virtual TimePoint now() const = 0;
+};
+
 class ServiceManagerWrapper {
 public:
     virtual ~ServiceManagerWrapper() = default;
@@ -170,6 +176,7 @@
     virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
     virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0;
     virtual std::unique_ptr<FsWrapper> getFs() = 0;
+    virtual std::unique_ptr<ClockWrapper> getClock() = 0;
 };
 
 // --- Real stuff ---
@@ -187,6 +194,7 @@
     std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
     std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final;
     std::unique_ptr<FsWrapper> getFs() final;
+    std::unique_ptr<ClockWrapper> getClock() final;
 
 private:
     template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 5236983..25b34b56 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -248,6 +248,27 @@
         }
         return binder::Status::ok();
     }
+    binder::Status bindToDataLoaderNotOkWithNoDelay(int32_t mountId,
+                                                    const DataLoaderParamsParcel& params,
+                                                    int bindDelayMs,
+                                                    const sp<IDataLoaderStatusListener>& listener,
+                                                    bool* _aidl_return) {
+        CHECK(bindDelayMs == 0) << bindDelayMs;
+        *_aidl_return = false;
+        return binder::Status::ok();
+    }
+    binder::Status bindToDataLoaderBindingWithNoDelay(int32_t mountId,
+                                                      const DataLoaderParamsParcel& params,
+                                                      int bindDelayMs,
+                                                      const sp<IDataLoaderStatusListener>& listener,
+                                                      bool* _aidl_return) {
+        CHECK(bindDelayMs == 0) << bindDelayMs;
+        *_aidl_return = true;
+        if (listener) {
+            listener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_BINDING);
+        }
+        return binder::Status::ok();
+    }
     binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId,
                                                   const DataLoaderParamsParcel& params,
                                                   int bindDelayMs,
@@ -557,6 +578,21 @@
     }
 };
 
+class MockClockWrapper : public ClockWrapper {
+public:
+    MOCK_CONST_METHOD0(now, TimePoint());
+
+    void start() { ON_CALL(*this, now()).WillByDefault(Invoke(this, &MockClockWrapper::getClock)); }
+    template <class Delta>
+    void advance(Delta delta) {
+        mClock += delta;
+    }
+
+    TimePoint getClock() const { return mClock; }
+
+    TimePoint mClock = Clock::now();
+};
+
 class MockStorageHealthListener : public os::incremental::BnStorageHealthListener {
 public:
     MOCK_METHOD2(onHealthStatus, binder::Status(int32_t storageId, int32_t status));
@@ -594,7 +630,7 @@
                        std::unique_ptr<MockLooperWrapper> looper,
                        std::unique_ptr<MockTimedQueueWrapper> timedQueue,
                        std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue,
-                       std::unique_ptr<MockFsWrapper> fs)
+                       std::unique_ptr<MockFsWrapper> fs, std::unique_ptr<MockClockWrapper> clock)
           : mVold(std::move(vold)),
             mDataLoaderManager(std::move(dataLoaderManager)),
             mIncFs(std::move(incfs)),
@@ -603,7 +639,8 @@
             mLooper(std::move(looper)),
             mTimedQueue(std::move(timedQueue)),
             mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)),
-            mFs(std::move(fs)) {}
+            mFs(std::move(fs)),
+            mClock(std::move(clock)) {}
     std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
     std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
         return std::move(mDataLoaderManager);
@@ -619,6 +656,7 @@
         return std::move(mProgressUpdateJobQueue);
     }
     std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
+    std::unique_ptr<ClockWrapper> getClock() final { return std::move(mClock); }
 
 private:
     std::unique_ptr<MockVoldService> mVold;
@@ -630,6 +668,7 @@
     std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
     std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue;
     std::unique_ptr<MockFsWrapper> mFs;
+    std::unique_ptr<MockClockWrapper> mClock;
 };
 
 // --- IncrementalServiceTest ---
@@ -657,6 +696,8 @@
         mProgressUpdateJobQueue = progressUpdateJobQueue.get();
         auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
         mFs = fs.get();
+        auto clock = std::make_unique<NiceMock<MockClockWrapper>>();
+        mClock = clock.get();
         mIncrementalService = std::make_unique<
                 IncrementalService>(MockServiceManager(std::move(vold),
                                                        std::move(dataloaderManager),
@@ -664,12 +705,13 @@
                                                        std::move(jni), std::move(looper),
                                                        std::move(timedQueue),
                                                        std::move(progressUpdateJobQueue),
-                                                       std::move(fs)),
+                                                       std::move(fs), std::move(clock)),
                                     mRootDir.path);
         mDataLoaderParcel.packageName = "com.test";
         mDataLoaderParcel.arguments = "uri";
         mDataLoaderManager->unbindFromDataLoaderSuccess();
         mIncrementalService->onSystemReady();
+        mClock->start();
         setupSuccess();
     }
 
@@ -724,6 +766,7 @@
     NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
     NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr;
     NiceMock<MockFsWrapper>* mFs = nullptr;
+    NiceMock<MockClockWrapper>* mClock = nullptr;
     NiceMock<MockDataLoader>* mDataLoader = nullptr;
     std::unique_ptr<IncrementalService> mIncrementalService;
     TemporaryDir mRootDir;
@@ -853,6 +896,119 @@
     mDataLoaderManager->setDataLoaderStatusDestroyed();
 }
 
+TEST_F(IncrementalServiceTest, testDataLoaderOnRestart) {
+    mIncFs->waitForPendingReadsSuccess();
+    mIncFs->openMountSuccess();
+
+    constexpr auto bindRetryInterval = 5s;
+
+    EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(10);
+    EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+    EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6);
+    EXPECT_CALL(*mDataLoader, start(_)).Times(6);
+    EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+    EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+    TemporaryDir tempDir;
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
+    ASSERT_GE(storageId, 0);
+
+    // First binds to DataLoader fails... because it's restart.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderNotOkWithNoDelay));
+
+    // Request DL start.
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
+
+    // Retry callback present.
+    ASSERT_EQ(storageId, mTimedQueue->mId);
+    ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+    auto retryCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // Expecting the same bindToDataLoaderNotOkWithNoDelay call.
+    mClock->advance(5s);
+
+    retryCallback();
+    // Retry callback present.
+    ASSERT_EQ(storageId, mTimedQueue->mId);
+    ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+    retryCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // Returning "binding" so that we can retry.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderBindingWithNoDelay));
+
+    // Expecting bindToDataLoaderBindingWithNoDelay call.
+    mClock->advance(5s);
+
+    retryCallback();
+    // No retry callback.
+    ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+    ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+    // Should not change the bindToDataLoader call count
+    ASSERT_NE(nullptr, mLooper->mCallback);
+    ASSERT_NE(nullptr, mLooper->mCallbackData);
+    auto looperCb = mLooper->mCallback;
+    auto looperCbData = mLooper->mCallbackData;
+    looperCb(-1, -1, looperCbData);
+
+    // Expecting the same bindToDataLoaderBindingWithNoDelay call.
+    mClock->advance(5s);
+
+    // Use pending reads callback to trigger binding.
+    looperCb(-1, -1, looperCbData);
+
+    // No retry callback.
+    ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+    ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+    // Now we are out of 10m "retry" budget, let's finally bind.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager, &MockDataLoaderManager::bindToDataLoaderOk));
+    mClock->advance(11min);
+
+    // Use pending reads callback to trigger binding.
+    looperCb(-1, -1, looperCbData);
+
+    // No retry callback.
+    ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+    ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+    // And test the rest of the backoff.
+    // Simulated crash/other connection breakage.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+}
+
 TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
     mDataLoader->initializeCreateOkNoStatus();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5fbf1c4..4e23609 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -116,6 +116,7 @@
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.compat.PlatformCompatNative;
+import com.android.server.connectivity.PacProxyService;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -1315,6 +1316,7 @@
         ConsumerIrService consumerIr = null;
         MmsServiceBroker mmsService = null;
         HardwarePropertiesManagerService hardwarePropertiesService = null;
+        PacProxyService pacProxyService = null;
 
         boolean disableSystemTextClassifier = SystemProperties.getBoolean(
                 "config.disable_systemtextclassifier", false);
@@ -1874,6 +1876,15 @@
                 t.traceEnd();
             }
 
+            t.traceBegin("StartPacProxyService");
+            try {
+                pacProxyService = new PacProxyService(context);
+                ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService);
+            } catch (Throwable e) {
+                reportWtf("starting PacProxyService", e);
+            }
+            t.traceEnd();
+
             t.traceBegin("StartConnectivityService");
             // This has to be called after NetworkManagementService, NetworkStatsService
             // and NetworkPolicyManager because ConnectivityService needs to take these
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
index 169b85e..b07fe19 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_SMARTSPACE;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.content.Context.SMARTSPACE_SERVICE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -161,11 +162,13 @@
                 Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
                         + ", uid=" + Binder.getCallingUid());
             }
-            if (!(mServiceNameResolver.isTemporary(userId)
+            Context ctx = getContext();
+            if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED
+                    || mServiceNameResolver.isTemporary(userId)
                     || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
 
-                String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
-                        + ", uid=" + Binder.getCallingUid();
+                String msg = "Permission Denial: Cannot call " + func + " from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
             }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 29db740..7e4bc1e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1880,6 +1880,144 @@
     }
 
     @Test
+    public void testIsWithinEJQuotaLocked_NeverApp() {
+        JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
+        setStandbyBucket(NEVER_INDEX, js);
+        synchronized (mQuotaController.mLock) {
+            assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+    }
+
+    @Test
+    public void testIsWithinEJQuotaLocked_Charging() {
+        setCharging();
+        JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_Charging", 1);
+        synchronized (mQuotaController.mLock) {
+            assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+    }
+
+    @Test
+    public void testIsWithinEJQuotaLocked_UnderDuration() {
+        setDischarging();
+        JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_UnderDuration", 1);
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+        synchronized (mQuotaController.mLock) {
+            assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+    }
+
+    @Test
+    public void testIsWithinEJQuotaLocked_OverDuration() {
+        setDischarging();
+        JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_OverDuration", 1);
+        setStandbyBucket(FREQUENT_INDEX, js);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+        synchronized (mQuotaController.mLock) {
+            assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+    }
+
+    @Test
+    public void testIsWithinEJQuotaLocked_TimingSession() {
+        setDischarging();
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 8 * MINUTE_IN_MILLIS);
+
+        JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TimingSession", 1);
+        for (int i = 0; i < 25; ++i) {
+            mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                    createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
+                            2), true);
+
+            synchronized (mQuotaController.mLock) {
+                setStandbyBucket(ACTIVE_INDEX, js);
+                assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
+                        i < 19, mQuotaController.isWithinEJQuotaLocked(js));
+
+                setStandbyBucket(WORKING_INDEX, js);
+                assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
+                        i < 14, mQuotaController.isWithinEJQuotaLocked(js));
+
+                setStandbyBucket(FREQUENT_INDEX, js);
+                assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
+                        i < 12, mQuotaController.isWithinEJQuotaLocked(js));
+
+                setStandbyBucket(RARE_INDEX, js);
+                assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
+                        i < 9, mQuotaController.isWithinEJQuotaLocked(js));
+
+                setStandbyBucket(RESTRICTED_INDEX, js);
+                assertEquals("Restricted has incorrect quota status with " + (i + 1) + " sessions",
+                        i < 7, mQuotaController.isWithinEJQuotaLocked(js));
+            }
+        }
+    }
+
+    /**
+     * Tests that Timers properly track sessions when an app is added and removed from the temp
+     * allowlist.
+     */
+    @Test
+    public void testIsWithinEJQuotaLocked_TempAllowlisting() {
+        setDischarging();
+        JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TempAllowlisting", 1);
+        setStandbyBucket(FREQUENT_INDEX, js);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+        synchronized (mQuotaController.mLock) {
+            assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
+        setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
+        Handler handler = mQuotaController.getHandler();
+        spyOn(handler);
+
+        // Apps on the temp allowlist should be able to schedule & start EJs, even if they're out
+        // of quota (as long as they are in the temp allowlist grace period).
+        mTempAllowlistListener.onAppAdded(mSourceUid);
+        synchronized (mQuotaController.mLock) {
+            assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mTempAllowlistListener.onAppRemoved(mSourceUid);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        // Still in grace period
+        synchronized (mQuotaController.mLock) {
+            assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+        advanceElapsedClock(6 * SECOND_IN_MILLIS);
+        // Out of grace period.
+        synchronized (mQuotaController.mLock) {
+            assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+        }
+    }
+
+    @Test
     public void testMaybeScheduleCleanupAlarmLocked() {
         // No sessions saved yet.
         synchronized (mQuotaController.mLock) {
@@ -3881,6 +4019,55 @@
     }
 
     @Test
+    public void testGetRemainingEJExecutionTimeLocked_IncrementalTimingSessions() {
+        setDischarging();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 20 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 15 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 13 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
+
+        for (int i = 1; i <= 25; ++i) {
+            mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                    createTimingSession(now - ((60 - i) * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS,
+                            2), true);
+
+            synchronized (mQuotaController.mLock) {
+                setStandbyBucket(ACTIVE_INDEX);
+                assertEquals("Active has incorrect remaining EJ time with " + i + " sessions",
+                        (20 - i) * MINUTE_IN_MILLIS,
+                        mQuotaController.getRemainingEJExecutionTimeLocked(
+                                SOURCE_USER_ID, SOURCE_PACKAGE));
+
+                setStandbyBucket(WORKING_INDEX);
+                assertEquals("Working has incorrect remaining EJ time with " + i + " sessions",
+                        (15 - i) * MINUTE_IN_MILLIS,
+                        mQuotaController.getRemainingEJExecutionTimeLocked(
+                                SOURCE_USER_ID, SOURCE_PACKAGE));
+
+                setStandbyBucket(FREQUENT_INDEX);
+                assertEquals("Frequent has incorrect remaining EJ time with " + i + " sessions",
+                        (13 - i) * MINUTE_IN_MILLIS,
+                        mQuotaController.getRemainingEJExecutionTimeLocked(
+                                SOURCE_USER_ID, SOURCE_PACKAGE));
+
+                setStandbyBucket(RARE_INDEX);
+                assertEquals("Rare has incorrect remaining EJ time with " + i + " sessions",
+                        (10 - i) * MINUTE_IN_MILLIS,
+                        mQuotaController.getRemainingEJExecutionTimeLocked(
+                                SOURCE_USER_ID, SOURCE_PACKAGE));
+
+                setStandbyBucket(RESTRICTED_INDEX);
+                assertEquals("Restricted has incorrect remaining EJ time with " + i + " sessions",
+                        (5 - i) * MINUTE_IN_MILLIS,
+                        mQuotaController.getRemainingEJExecutionTimeLocked(
+                                SOURCE_USER_ID, SOURCE_PACKAGE));
+            }
+        }
+    }
+
+    @Test
     public void testGetTimeUntilEJQuotaConsumedLocked_NoHistory() {
         final long[] limits = mQuotaController.getEJLimitsMs();
         for (int i = 0; i < limits.length; ++i) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 129d263..e13597d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -19,23 +19,37 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.OnUidImportanceListener;
 import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningAppProcessInfo;
 import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.uiautomator.UiDevice;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
@@ -65,6 +79,12 @@
     private static final long AWAIT_TIMEOUT = 2000;
     private static final long CHECK_INTERVAL = 100;
 
+    private static final String TEST_FGS_CLASS =
+            "com.android.servicestests.apps.simpleservicetestapp.SimpleFgService";
+    private static final String ACTION_FGS_STATS_TEST =
+            "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+    private static final String EXTRA_MESSENGER = "extra_messenger";
+
     private IActivityManager mService;
     private IRemoteCallback mCallback;
     private Context mContext;
@@ -204,4 +224,184 @@
         public void onServiceDisconnected(ComponentName name) {
         }
     }
+
+    /**
+     * Note: This test actually only works in eng build. It'll always pass
+     * in user and userdebug build, because the expected exception won't be
+     * thrown in those builds.
+     */
+    @LargeTest
+    @Test
+    public void testFgsProcStatsTracker() throws Exception {
+        final PackageManager pm = mContext.getPackageManager();
+        final long timeout = 5000;
+        int uid = pm.getPackageUid(TEST_APP, 0);
+        final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
+        final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
+        final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        final CountDownLatch[] latchHolder = new CountDownLatch[1];
+        final H handler = new H(Looper.getMainLooper(), latchHolder);
+        final Messenger messenger = new Messenger(handler);
+        final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+        final CountDownLatch dboxLatch = new CountDownLatch(1);
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String tag_wtf = "system_server_wtf";
+                if (tag_wtf.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
+                    final DropBoxManager.Entry e = dbox.getNextEntry(tag_wtf, intent.getLongExtra(
+                            DropBoxManager.EXTRA_TIME, 0) - 1);
+                    final String text = e.getText(8192);
+                    if (TextUtils.isEmpty(text)) {
+                        return;
+                    }
+                    if (text.indexOf("can't store negative values") == -1) {
+                        return;
+                    }
+                    dboxLatch.countDown();
+                }
+            }
+        };
+        try {
+            mContext.registerReceiver(receiver,
+                    new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
+            am.addOnUidImportanceListener(uidListener1,
+                    RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+            am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+            runShellCommand("cmd deviceidle whitelist +" + TEST_APP);
+            toggleScreenOn(true);
+
+            final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
+            final ComponentName cn = ComponentName.unflattenFromString(
+                    TEST_APP + "/" + TEST_FGS_CLASS);
+            final Bundle bundle = new Bundle();
+            intent.setComponent(cn);
+            bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
+            intent.putExtras(bundle);
+
+            latchHolder[0] = new CountDownLatch(1);
+            mContext.startForegroundService(intent);
+            assertTrue("Timed out to start fg service", uidListener1.waitFor(
+                    RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, timeout));
+            assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+                    timeout, TimeUnit.MILLISECONDS));
+
+            Thread.sleep(timeout);
+            latchHolder[0] = new CountDownLatch(1);
+            handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+            assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+                    timeout, TimeUnit.MILLISECONDS));
+
+            Thread.sleep(timeout);
+            latchHolder[0] = new CountDownLatch(1);
+            handler.sendRemoteMessage(H.MSG_START_FOREGROUND, 0, 0, null);
+            assertTrue("Timed out to wait for start fg", latchHolder[0].await(
+                    timeout, TimeUnit.MILLISECONDS));
+
+            toggleScreenOn(false);
+            latchHolder[0] = new CountDownLatch(1);
+            handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+            assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+                    timeout, TimeUnit.MILLISECONDS));
+            assertFalse("There shouldn't be negative values", dboxLatch.await(
+                    timeout * 2, TimeUnit.MILLISECONDS));
+        } finally {
+            toggleScreenOn(true);
+            runShellCommand("cmd deviceidle whitelist -" + TEST_APP);
+            am.removeOnUidImportanceListener(uidListener1);
+            am.removeOnUidImportanceListener(uidListener2);
+            am.forceStopPackage(TEST_APP);
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    /**
+     * Make sure the screen state.
+     */
+    private void toggleScreenOn(final boolean screenon) throws Exception {
+        if (screenon) {
+            runShellCommand("input keyevent KEYCODE_WAKEUP");
+            runShellCommand("wm dismiss-keyguard");
+        } else {
+            runShellCommand("input keyevent KEYCODE_SLEEP");
+        }
+        // Since the screen on/off intent is ordered, they will not be sent right now.
+        Thread.sleep(2_000);
+    }
+
+    private class H extends Handler {
+        static final int MSG_INIT = 0;
+        static final int MSG_DONE = 1;
+        static final int MSG_START_FOREGROUND = 2;
+        static final int MSG_STOP_FOREGROUND = 3;
+
+        private Messenger mRemoteMessenger;
+        private CountDownLatch[] mLatchHolder;
+
+        H(Looper looper, CountDownLatch[] latchHolder) {
+            super(looper);
+            mLatchHolder = latchHolder;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_INIT:
+                    mRemoteMessenger = (Messenger) msg.obj;
+                    mLatchHolder[0].countDown();
+                    break;
+                case MSG_DONE:
+                    mLatchHolder[0].countDown();
+                    break;
+            }
+        }
+
+        void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+            Message msg = Message.obtain();
+            msg.what = what;
+            msg.arg1 = arg1;
+            msg.arg2 = arg2;
+            msg.obj = obj;
+            try {
+                mRemoteMessenger.send(msg);
+            } catch (RemoteException e) {
+            }
+            msg.recycle();
+        }
+    }
+
+    private static class MyUidImportanceListener implements OnUidImportanceListener {
+        final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
+        private final int mExpectedUid;
+        private int mExpectedImportance;
+        private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+        MyUidImportanceListener(int uid) {
+            mExpectedUid = uid;
+        }
+
+        @Override
+        public void onUidImportance(int uid, int importance) {
+            if (uid == mExpectedUid) {
+                synchronized (this) {
+                    if (importance == mExpectedImportance && mLatchHolder[0] != null) {
+                        mLatchHolder[0].countDown();
+                    }
+                    mCurrentImportance = importance;
+                }
+                Log.i(TAG, "uid " + uid + " importance: " + importance);
+            }
+        }
+
+        boolean waitFor(int expectedImportance, long timeout) throws Exception {
+            synchronized (this) {
+                mExpectedImportance = expectedImportance;
+                if (mCurrentImportance == expectedImportance) {
+                    return true;
+                }
+                mLatchHolder[0] = new CountDownLatch(1);
+            }
+            return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 07f6732..1c96838 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -40,9 +40,11 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import org.junit.Before;
@@ -60,6 +62,7 @@
  * Tests for {@link com.android.server.apphibernation.AppHibernationService}
  */
 @SmallTest
+@Presubmit
 public final class AppHibernationServiceTest {
     private static final String PACKAGE_SCHEME = "package";
     private static final String PACKAGE_NAME_1 = "package1";
@@ -91,6 +94,7 @@
         MockitoAnnotations.initMocks(this);
         doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
 
+        LocalServices.removeServiceForTest(AppHibernationManagerInternal.class);
         mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
 
         verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
index 59f3c35..2237c84 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
@@ -48,6 +49,7 @@
 
 
 @SmallTest
+@Presubmit
 public class HibernationStateDiskStoreTest {
     private static final String STATES_FILE_NAME = "states";
     private final MockScheduledExecutorService mMockScheduledExecutorService =
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 6890ed1..c84c1cf 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -415,9 +415,7 @@
                                 + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
                 i++) {
             GenericDocument document =
-                    new GenericDocument.Builder<>("uri" + i, "type")
-                            .setNamespace("namespace")
-                            .build();
+                    new GenericDocument.Builder<>("namespace", "uri" + i, "type").build();
             mAppSearchImpl.putDocument("package", "database", document);
         }
 
@@ -477,7 +475,7 @@
 
         // Insert document
         GenericDocument document =
-                new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "type").build();
         mAppSearchImpl.putDocument("package", "database", document);
 
         // Rewrite SearchSpec
@@ -517,11 +515,11 @@
 
         // Insert documents
         GenericDocument document1 =
-                new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "typeA").build();
         mAppSearchImpl.putDocument("package", "database1", document1);
 
         GenericDocument document2 =
-                new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "typeB").build();
         mAppSearchImpl.putDocument("package", "database2", document2);
 
         // Rewrite SearchSpec
@@ -561,7 +559,7 @@
 
         // Insert document
         GenericDocument document =
-                new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "type").build();
         mAppSearchImpl.putDocument("package", "database", document);
 
         // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to
@@ -614,7 +612,7 @@
 
         // Insert package1 document
         GenericDocument document =
-                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
         mAppSearchImpl.putDocument("package1", "database1", document);
 
         // No query filters specified, package2 shouldn't be able to query for package1's documents.
@@ -625,14 +623,13 @@
         assertThat(searchResultPage.getResults()).isEmpty();
 
         // Insert package2 document
-        document =
-                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+        document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
         mAppSearchImpl.putDocument("package2", "database2", document);
 
         // No query filters specified. package2 should only get its own documents back.
         searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
         assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
     }
 
     /**
@@ -665,7 +662,7 @@
 
         // Insert package1 document
         GenericDocument document =
-                new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+                new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
         mAppSearchImpl.putDocument("package1", "database1", document);
 
         // "package1" filter specified, but package2 shouldn't be able to query for package1's
@@ -680,8 +677,7 @@
         assertThat(searchResultPage.getResults()).isEmpty();
 
         // Insert package2 document
-        document =
-                new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+        document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
         mAppSearchImpl.putDocument("package2", "database2", document);
 
         // "package2" filter specified, package2 should only get its own documents back.
@@ -692,7 +688,7 @@
                         .build();
         searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
         assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
     }
 
     @Test
@@ -1073,7 +1069,7 @@
         for (SearchResult result : searchResultPage.getResults()) {
             assertThat(result.getPackageName()).isEqualTo("com.package.foo");
             assertThat(result.getDatabaseName()).isEqualTo("databaseName");
-            assertThat(result.getDocument())
+            assertThat(result.getGenericDocument())
                     .isEqualTo(
                             GenericDocumentToProtoConverter.toGenericDocument(
                                     strippedDocumentProto.build()));
@@ -1128,9 +1124,7 @@
                     appSearchImpl.putDocument(
                             "package",
                             "database",
-                            new GenericDocument.Builder<>("uri", "type")
-                                    .setNamespace("namespace")
-                                    .build());
+                            new GenericDocument.Builder<>("namespace", "uri", "type").build());
                 });
 
         expectThrows(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 194be37..70e1e05 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -36,23 +36,23 @@
     private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
     private static final GenericDocument DOCUMENT_PROPERTIES_1 =
             new GenericDocument.Builder<GenericDocument.Builder<?>>(
-                            "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+                            "namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
                     .setCreationTimestampMillis(12345L)
                     .build();
     private static final GenericDocument DOCUMENT_PROPERTIES_2 =
             new GenericDocument.Builder<GenericDocument.Builder<?>>(
-                            "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+                            "namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
                     .setCreationTimestampMillis(6789L)
                     .build();
 
     @Test
     public void testDocumentProtoConvert() {
         GenericDocument document =
-                new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1")
+                new GenericDocument.Builder<GenericDocument.Builder<?>>(
+                                "namespace", "uri1", "schemaType1")
                         .setCreationTimestampMillis(5L)
                         .setScore(1)
                         .setTtlMillis(1L)
-                        .setNamespace("namespace")
                         .setPropertyLong("longKey1", 1L)
                         .setPropertyDouble("doubleKey1", 1.0)
                         .setPropertyBoolean("booleanKey1", true)
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 0b1c120..d07211f 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
@@ -93,10 +93,10 @@
             assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
             assertThat(match.getFullText()).isEqualTo(propertyValueString);
             assertThat(match.getExactMatch()).isEqualTo(exactMatch);
-            assertThat(match.getExactMatchPosition())
+            assertThat(match.getExactMatchRange())
                     .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32));
             assertThat(match.getFullText()).isEqualTo(propertyValueString);
-            assertThat(match.getSnippetPosition())
+            assertThat(match.getSnippetRange())
                     .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32));
             assertThat(match.getSnippet()).isEqualTo(window);
         }
@@ -210,20 +210,20 @@
             SearchResult.MatchInfo match1 = result.getMatches().get(0);
             assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
             assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
-            assertThat(match1.getExactMatchPosition())
+            assertThat(match1.getExactMatchRange())
                     .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4));
             assertThat(match1.getExactMatch()).isEqualTo("Test");
-            assertThat(match1.getSnippetPosition())
+            assertThat(match1.getSnippetRange())
                     .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
             assertThat(match1.getSnippet()).isEqualTo("Test Name");
 
             SearchResult.MatchInfo match2 = result.getMatches().get(1);
             assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
             assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
-            assertThat(match2.getExactMatchPosition())
+            assertThat(match2.getExactMatchRange())
                     .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
             assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
-            assertThat(match2.getSnippetPosition())
+            assertThat(match2.getSnippetRange())
                     .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
             assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
         }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
new file mode 100644
index 0000000..7afcbf7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.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 com.android.server.appsearch.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.SparseIntArray;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.appsearch.external.localstorage.MockPackageManager;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class PlatformLoggerTest {
+    private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
+    private static final int TEST_DEFAULT_SAMPLING_RATIO = 10;
+    private static final String TEST_PACKAGE_NAME = "packageName";
+    private MockPackageManager mMockPackageManager = new MockPackageManager();
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+        mContext =
+                new ContextWrapper(context) {
+                    @Override
+                    public PackageManager getPackageManager() {
+                        return mMockPackageManager.getMockPackageManager();
+                    }
+                };
+    }
+
+    @Test
+    public void testcreateExtraStatsLocked_nullSamplingRatioMap_returnsDefaultSamplingRatio() {
+        PlatformLogger logger = new PlatformLogger(
+                ApplicationProvider.getApplicationContext(),
+                UserHandle.USER_NULL,
+                new PlatformLogger.Config(
+                        TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+                        TEST_DEFAULT_SAMPLING_RATIO,
+                        /*samplingRatioMap=*/ null));
+
+        // Make sure default sampling ratio is used if samplingMap is not provided.
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_UNKNOWN).mSamplingRatio).isEqualTo(
+                TEST_DEFAULT_SAMPLING_RATIO);
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
+                TEST_DEFAULT_SAMPLING_RATIO);
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+                TEST_DEFAULT_SAMPLING_RATIO);
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
+                TEST_DEFAULT_SAMPLING_RATIO);
+    }
+
+
+    @Test
+    public void testcreateExtraStatsLocked_with_samplingRatioMap_returnsConfiguredSamplingRatio() {
+        int putDocumentSamplingRatio = 1;
+        int querySamplingRatio = 2;
+        final SparseIntArray samplingRatios = new SparseIntArray();
+        samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio);
+        samplingRatios.put(CallStats.CALL_TYPE_QUERY, querySamplingRatio);
+        PlatformLogger logger = new PlatformLogger(
+                ApplicationProvider.getApplicationContext(),
+                UserHandle.USER_NULL,
+                new PlatformLogger.Config(
+                        TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+                        TEST_DEFAULT_SAMPLING_RATIO,
+                        samplingRatios));
+
+         // The default sampling ratio should be used if no sampling ratio is
+         // provided for certain call type.
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
+                TEST_DEFAULT_SAMPLING_RATIO);
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
+                TEST_DEFAULT_SAMPLING_RATIO);
+
+        // The configured sampling ratio is used if sampling ratio is available
+        // for certain call type.
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo(
+                putDocumentSamplingRatio);
+        assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
+                CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+                querySamplingRatio);
+    }
+
+    @Test
+    public void testShouldLogForTypeLocked_trueWhenSampleRatioIsOne() {
+        final int samplingRatio = 1;
+        final String testPackageName = "packageName";
+        PlatformLogger logger = new PlatformLogger(
+                ApplicationProvider.getApplicationContext(),
+                UserHandle.USER_NULL,
+                new PlatformLogger.Config(
+                        TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+                        samplingRatio,
+                        /* samplingMap=*/ null));
+
+        // Sample should always be logged for the first time if sampling is disabled(value is one).
+        assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
+        assertThat(logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+    }
+
+    @Test
+    public void testShouldLogForTypeLocked_falseWhenSampleRatioIsNegative() {
+        final int samplingRatio = -1;
+        final String testPackageName = "packageName";
+        PlatformLogger logger = new PlatformLogger(
+                ApplicationProvider.getApplicationContext(),
+                UserHandle.USER_NULL,
+                new PlatformLogger.Config(
+                        TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+                        samplingRatio,
+                        /* samplingMap=*/ null));
+
+        // Makes sure sample will be excluded due to sampling if sample ratio is negative.
+        assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
+        // Skipped count should be 0 since it doesn't pass the sampling.
+        assertThat(logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+    }
+
+    @Test
+    public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() {
+        // Next sample won't be excluded due to sampling.
+        final int samplingRatio = 1;
+        // Next sample would guaranteed to be too close.
+        final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE;
+        final String testPackageName = "packageName";
+        PlatformLogger logger = new PlatformLogger(
+                ApplicationProvider.getApplicationContext(),
+                UserHandle.USER_NULL,
+                new PlatformLogger.Config(
+                        minTimeIntervalBetweenSamplesMillis,
+                        samplingRatio,
+                        /* samplingMap=*/ null));
+        logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
+
+        // Makes sure sample will be excluded due to rate limiting if samples are too close.
+        assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
+        assertThat(logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1);
+    }
+
+    @Test
+    public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() {
+        // Next sample won't be excluded due to sampling.
+        final int samplingRatio = 1;
+        // Next sample would guaranteed to be included.
+        final int minTimeIntervalBetweenSamplesMillis = 0;
+        final String testPackageName = "packageName";
+        PlatformLogger logger = new PlatformLogger(
+                ApplicationProvider.getApplicationContext(),
+                UserHandle.USER_NULL,
+                new PlatformLogger.Config(
+                        minTimeIntervalBetweenSamplesMillis,
+                        samplingRatio,
+                        /* samplingMap=*/ null));
+        logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
+
+        // Makes sure sample will be logged if it is not too close to previous sample.
+        assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
+        assertThat(logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0);
+    }
+
+    /** Makes sure the caching works while getting the UID for calling package. */
+    @Test
+    public void testGetPackageUidAsUser() throws Exception {
+        final String testPackageName = "packageName";
+        final int testUid = 1234;
+        PlatformLogger logger = new PlatformLogger(
+                mContext,
+                mContext.getUserId(),
+                new PlatformLogger.Config(
+                        TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
+                        TEST_DEFAULT_SAMPLING_RATIO,
+                        /* samplingMap=*/ null));
+        mMockPackageManager.mockGetPackageUidAsUser(testPackageName, mContext.getUserId(), testUid);
+
+        //
+        // First time, no cache
+        //
+        PlatformLogger.ExtraStats extraStats = logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+        verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
+                eq(testPackageName), /*userId=*/ anyInt());
+        assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+
+        //
+        // Second time, we have cache
+        //
+        extraStats = logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+        // Count is still one since we will use the cache
+        verify(mMockPackageManager.getMockPackageManager(), times(1)).getPackageUidAsUser(
+                eq(testPackageName), /*userId=*/ anyInt());
+        assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+
+        //
+        // Remove the cache and try again
+        //
+        assertThat(logger.removeCachedUidForPackage(testPackageName)).isEqualTo(testUid);
+        extraStats = logger.createExtraStatsLocked(testPackageName,
+                CallStats.CALL_TYPE_PUT_DOCUMENT);
+
+        // count increased by 1 since cache is cleared
+        verify(mMockPackageManager.getMockPackageManager(), times(2)).getPackageUidAsUser(
+                eq(testPackageName), /*userId=*/ anyInt());
+        assertThat(extraStats.mPackageUid).isEqualTo(testUid);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index f00edcc..fcd6b84 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -120,6 +120,11 @@
         return this;
     }
 
+    CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
+        return this;
+    }
+
     CompatConfig build() {
         CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
         config.forceNonDebuggableFinalForTest(false);
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index b6b6932..bd77405 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -86,6 +86,7 @@
         // Assume userdebug/eng non-final build
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
         ChangeIdStateCache.disable();
         when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
                 .thenThrow(new NameNotFoundException());
@@ -567,6 +568,34 @@
     }
 
     @Test
+    public void testReadApexConfig() throws IOException {
+        String configXml = "<config>"
+                + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+                + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+                + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+                + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />"
+                + "</config>";
+
+        File dir = createTempDir();
+        writeToFile(dir, "platform_compat_config.xml", configXml);
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
+        compatConfig.initConfigFromLib(dir);
+
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1235L,
+            ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1236L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1237L,
+            ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue();
+    }
+
+    @Test
     public void testReadConfigMultipleFiles() throws IOException {
         String configXml1 = "<config>"
                 + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 0fd6445..57fdcd3 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -22,6 +22,7 @@
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -52,6 +53,7 @@
     private static final int TARGET_SDK = 10;
     private static final int TARGET_SDK_BEFORE = 9;
     private static final int TARGET_SDK_AFTER = 11;
+    private static final int PLATFORM_SDK_VERSION = 30;
 
     @Mock
     private PackageManager mPackageManager;
@@ -61,6 +63,7 @@
     private AndroidBuildClassifier debuggableBuild() {
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -68,6 +71,7 @@
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(false);
         when(buildClassifier.isFinalBuild()).thenReturn(false);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -75,6 +79,7 @@
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(false);
         when(buildClassifier.isFinalBuild()).thenReturn(true);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -333,6 +338,26 @@
     }
 
     @Test
+    public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride()
+            throws Exception {
+        final AndroidBuildClassifier buildClassifier = finalBuild();
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable()
+                        .build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        assertThat(stateTargetSdkLessChange).isEqualTo(
+                new OverrideAllowedState(PLATFORM_TOO_OLD, -1,
+                                         PLATFORM_SDK_VERSION));
+    }
+
+    @Test
     public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 799b067..3fc6e99 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -78,11 +78,12 @@
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
             .thenThrow(new PackageManager.NameNotFoundException());
         mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         // Assume userdebug/eng non-final build
         mCompatConfig.forceNonDebuggableFinalForTest(false);
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
     }
@@ -99,7 +100,7 @@
                 .addLoggingOnlyChangeWithId(7L)
                 .addOverridableChangeWithId(8L)
                 .build();
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -125,8 +126,9 @@
                 .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
                 .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
+                .addEnableSinceSdkChangeWithId(31, 8L)
                 .build();
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -144,7 +146,7 @@
                 .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
                 .build();
         mCompatConfig.forceNonDebuggableFinalForTest(true);
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
 
         // Before adding overrides.
         assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 87100a6..77a39d8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -107,6 +107,7 @@
 import android.telephony.data.ApnSetting;
 import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
@@ -154,6 +155,9 @@
 @SmallTest
 @Presubmit
 public class DevicePolicyManagerTest extends DpmTestBase {
+
+    private static final String TAG = DevicePolicyManagerTest.class.getSimpleName();
+
     private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
             permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
             permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
@@ -3875,7 +3879,7 @@
     }
 
     @Test
-    public void testForceUpdateUserSetupComplete_userbuild() {
+    public void testForceUpdateUserSetupComplete_forcesUpdate() {
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
@@ -3888,34 +3892,6 @@
         userData.mUserSetupComplete = true;
         dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
 
-        // GIVEN it's user build
-        getServices().buildMock.isDebuggable = false;
-
-        assertThat(dpms.hasUserSetupCompleted()).isTrue();
-
-        dpm.forceUpdateUserSetupComplete();
-
-        // THEN the state in dpms is not changed
-        assertThat(dpms.hasUserSetupCompleted()).isTrue();
-    }
-
-    @Test
-    public void testForceUpdateUserSetupComplete_userDebugbuild() {
-        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
-        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
-
-        final int userId = UserHandle.USER_SYSTEM;
-        // GIVEN userComplete is false in SettingsProvider
-        setUserSetupCompleteForUser(false, userId);
-
-        // GIVEN userComplete is true in DPM
-        DevicePolicyData userData = new DevicePolicyData(userId);
-        userData.mUserSetupComplete = true;
-        dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
-
-        // GIVEN it's userdebug build
-        getServices().buildMock.isDebuggable = true;
-
         assertThat(dpms.hasUserSetupCompleted()).isTrue();
 
         dpm.forceUpdateUserSetupComplete();
@@ -7215,6 +7191,47 @@
         assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled);
     }
 
+    @Test
+    public void testGetPolicyExemptApps_noPermission() {
+        assertThrows(SecurityException.class, () -> dpm.getPolicyExemptApps());
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_empty() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps();
+        mockVendorPolicyExemptApps();
+
+        assertThat(dpm.getPolicyExemptApps()).isEmpty();
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_baseOnly() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps("foo");
+        mockVendorPolicyExemptApps();
+
+        assertThat(dpm.getPolicyExemptApps()).containsExactly("foo");
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_vendorOnly() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps();
+        mockVendorPolicyExemptApps("bar");
+
+        assertThat(dpm.getPolicyExemptApps()).containsExactly("bar");
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_baseAndVendor() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps("4", "23", "15", "42", "8");
+        mockVendorPolicyExemptApps("16", "15", "4");
+
+        assertThat(dpm.getPolicyExemptApps()).containsExactly("4", "8", "15", "16", "23", "42");
+    }
+
     private void setUserUnlocked(int userHandle, boolean unlocked) {
         when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
     }
@@ -7436,4 +7453,18 @@
         return new StringParceledListSlice(Arrays.asList(s));
     }
 
+    private void grantManageDeviceAdmins() {
+        Log.d(TAG, "Granting " + permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+    }
+
+    private void mockPolicyExemptApps(String... apps) {
+        Log.d(TAG, "Mocking R.array.policy_exempt_apps to return " + Arrays.toString(apps));
+        when(mContext.resources.getStringArray(R.array.policy_exempt_apps)).thenReturn(apps);
+    }
+
+    private void mockVendorPolicyExemptApps(String... apps) {
+        Log.d(TAG, "Mocking R.array.vendor_policy_exempt_apps to return " + Arrays.toString(apps));
+        when(mContext.resources.getStringArray(R.array.vendor_policy_exempt_apps)).thenReturn(apps);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index f94b800a..2fe2f40 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -21,6 +21,11 @@
 import android.app.admin.DeviceAdminInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.os.Parcel;
+import android.util.TypedXmlPullParser;
+import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -32,9 +37,14 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Map;
@@ -44,18 +54,22 @@
 public class PolicyVersionUpgraderTest {
     // NOTE: Only change this value if the corresponding CL also adds a test to test the upgrade
     // to the new version.
-    private static final int LATEST_TESTED_VERSION = 1;
+    private static final int LATEST_TESTED_VERSION = 2;
+    public static final String PERMISSIONS_TAG = "admin-can-grant-sensors-permissions";
+    private ComponentName mFakeAdmin;
 
     private static class FakePolicyUpgraderDataProvider implements PolicyUpgraderDataProvider {
         int mDeviceOwnerUserId;
+        ComponentName mDeviceOwnerComponent = new ComponentName("", "");
         boolean mIsFileBasedEncryptionEnabled;
         Map<Integer, ComponentName> mUserToComponent = new HashMap<>();
-        Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo;
+        Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo = new HashMap<>();
         File mDataDir;
+        int[] mUsers;
 
         @Override
-        public boolean isUserDeviceOwner(int userId) {
-            return userId == mDeviceOwnerUserId;
+        public boolean isDeviceOwner(int userId, ComponentName who) {
+            return userId == mDeviceOwnerUserId && mDeviceOwnerComponent.equals(who);
         }
 
         @Override
@@ -92,6 +106,11 @@
         public Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId) {
             return componentName -> mComponentToDeviceAdminInfo.get(componentName);
         }
+
+        @Override
+        public int[] getUsersForUpgrade() {
+            return mUsers;
+        }
     }
 
     private final Context mRealTestContext = InstrumentationRegistry.getTargetContext();
@@ -105,38 +124,69 @@
         mUpgrader = new PolicyVersionUpgrader(mProvider);
         mDataDir = new File(mRealTestContext.getCacheDir(), "test-data");
         mDataDir.getParentFile().mkdirs();
+        // Prepare provider.
         mProvider.mDataDir = mDataDir;
+        mFakeAdmin = new ComponentName(
+                "com.android.frameworks.servicestests",
+                        "com.android.server.devicepolicy.DummyDeviceAdmins$Admin1");
+        ActivityInfo activityInfo = createActivityInfo(mFakeAdmin);
+        DeviceAdminInfo dai = createDeviceAdminInfo(activityInfo);
+        mProvider.mComponentToDeviceAdminInfo.put(mFakeAdmin, dai);
+        mProvider.mUsers = new int[] {0};
     }
 
     @Test
     public void testSameVersionDoesNothing() throws IOException {
-        int[] users = new int[] {0};
         writeVersionToXml(DevicePolicyManagerService.DPMS_VERSION);
-        preparePoliciesFile(users[0]);
-        String oldContents = readPoliciesFile(0);
+        final int userId = mProvider.mUsers[0];
+        preparePoliciesFile(userId);
+        String oldContents = readPoliciesFile(userId);
 
-        mUpgrader.upgradePolicy(users, DevicePolicyManagerService.DPMS_VERSION);
+        mUpgrader.upgradePolicy(DevicePolicyManagerService.DPMS_VERSION);
 
         String newContents = readPoliciesFile(0);
         assertThat(newContents).isEqualTo(oldContents);
     }
 
     @Test
-    public void testUpgrade0To1RemovesPasswordMetrics() throws IOException {
-        int[] users = new int[] {0, 10};
+    public void testUpgrade0To1RemovesPasswordMetrics() throws IOException, XmlPullParserException {
+        final String activePasswordTag = "active-password";
+        mProvider.mUsers = new int[] {0, 10};
         writeVersionToXml(0);
-        for (int userId : users) {
+        for (int userId : mProvider.mUsers) {
             preparePoliciesFile(userId);
         }
+        // Validate test set-up.
+        assertThat(isTagPresent(readPoliciesFileToStream(0), activePasswordTag)).isTrue();
 
-        String oldContents = readPoliciesFile(0);
-        assertThat(oldContents).contains("active-password");
+        mUpgrader.upgradePolicy(1);
 
-        mUpgrader.upgradePolicy(users, 1);
+        assertThat(readVersionFromXml()).isGreaterThan(1);
+        for (int user: mProvider.mUsers) {
+            assertThat(isTagPresent(readPoliciesFileToStream(user), activePasswordTag)).isFalse();
+        }
+    }
 
-        assertThat(readVersionFromXml()).isEqualTo(1);
-        assertThat(readPoliciesFile(users[0])).doesNotContain("active-password");
-        assertThat(readPoliciesFile(users[1])).doesNotContain("active-password");
+    @Test
+    public void testUpgrade1To2MarksDoForPermissionControl()
+            throws IOException, XmlPullParserException {
+        final int ownerUser = 10;
+        mProvider.mUsers = new int[] {0, ownerUser};
+        writeVersionToXml(1);
+        for (int userId : mProvider.mUsers) {
+            preparePoliciesFile(userId);
+        }
+        mProvider.mDeviceOwnerUserId = ownerUser;
+        mProvider.mDeviceOwnerComponent = mFakeAdmin;
+        mProvider.mUserToComponent.put(ownerUser, mFakeAdmin);
+
+        mUpgrader.upgradePolicy(2);
+
+        assertThat(readVersionFromXml()).isEqualTo(2);
+        assertThat(getBooleanValueTag(readPoliciesFileToStream(mProvider.mUsers[0]),
+                PERMISSIONS_TAG)).isFalse();
+        assertThat(getBooleanValueTag(readPoliciesFileToStream(ownerUser),
+                PERMISSIONS_TAG)).isTrue();
     }
 
     @Test
@@ -169,6 +219,70 @@
 
     private String readPoliciesFile(int userId) throws IOException {
         File policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId).chooseForRead();
-        return new String(Files.asByteSource(policiesFile).read());
+        FileReader reader = new FileReader(policiesFile);
+        return new String(Files.asByteSource(policiesFile).read(), Charset.defaultCharset());
+    }
+
+    private InputStream readPoliciesFileToStream(int userId) throws IOException {
+        File policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId).chooseForRead();
+        return new FileInputStream(policiesFile);
+    }
+
+    private boolean getBooleanValueTag(InputStream inputXml, String tagName)
+            throws IOException, XmlPullParserException {
+        TypedXmlPullParser parser = Xml.resolvePullParser(inputXml);
+
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                String tag = parser.getName();
+                if (tagName.equals(tag)) {
+                    String res = parser.getAttributeValue(null, "value");
+                    return Boolean.parseBoolean(res);
+                }
+            }
+            eventType = parser.next();
+        }
+
+        throw new IllegalStateException("Could not find " + tagName);
+    }
+
+    private boolean isTagPresent(InputStream inputXml, String tagName)
+            throws IOException, XmlPullParserException {
+        TypedXmlPullParser parser = Xml.resolvePullParser(inputXml);
+
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                String tag = parser.getName();
+                if (tagName.equals(tag)) {
+                    return true;
+                }
+            }
+            eventType = parser.next();
+        }
+
+        return false;
+    }
+
+    private ActivityInfo createActivityInfo(ComponentName admin) {
+        ActivityInfo ai = new ActivityInfo();
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.className = admin.getClassName();
+        applicationInfo.uid = 2222;
+        ai.applicationInfo = applicationInfo;
+        ai.name = admin.getClassName();
+        ai.packageName = admin.getPackageName();
+        return ai;
+    }
+
+    private DeviceAdminInfo createDeviceAdminInfo(ActivityInfo activityInfo) {
+        Parcel parcel = Parcel.obtain();
+        activityInfo.writeToParcel(parcel, 0);
+        parcel.writeInt(0);
+        parcel.writeBoolean(true);
+        parcel.setDataPosition(0);
+
+        return DeviceAdminInfo.CREATOR.createFromParcel(parcel);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 54825ee..c1b6101 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -65,6 +65,7 @@
     @Mock HysteresisLevels mScreenBrightnessThresholds;
     @Mock Handler mNoOpHandler;
     @Mock DisplayDevice mDisplayDevice;
+    @Mock HighBrightnessModeController mHbmController;
 
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
     @Before
@@ -90,7 +91,8 @@
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
-                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay, mContext
+                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay,
+                mContext, mHbmController
         );
         controller.setLoggingEnabled(true);
 
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 843296e..dbb415c 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -68,7 +68,7 @@
      */
     private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser {
         @Override
-        public String getPostScriptName(File file) throws IOException {
+        public String getCanonicalFileName(File file) throws IOException {
             String content = FileUtils.readTextFile(file, 100, "");
             return content.split(",")[0];
         }
@@ -160,10 +160,10 @@
         assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
                 .isEqualTo(expectedModifiedDate);
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -214,10 +214,10 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -246,10 +246,10 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -279,10 +279,10 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -332,14 +332,14 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "</family>")));
         try {
             dirForPreparation.update(Arrays.asList(
-                    newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
-                    newFontUpdateRequest("bar,2", "Invalid signature"),
+                    newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE),
+                    newFontUpdateRequest("bar.ttf,2", "Invalid signature"),
                     newAddFontFamilyRequest("<family name='foobar'>"
                             + "  <font>foo.ttf</font>"
                             + "  <font>bar.ttf</font>"
@@ -372,7 +372,7 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
         File fontFile = dir.getFontFileMap().get("test.ttf");
@@ -390,9 +390,9 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE)));
         Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
         assertThat(mapBeforeUpgrade).containsKey("test.ttf");
@@ -409,9 +409,10 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE)));
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -430,8 +431,8 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
-        dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -448,8 +449,8 @@
         dir.loadFontFileMap();
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -467,7 +468,8 @@
 
         try {
             dir.update(
-                    Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature")));
+                    Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+                            "Invalid signature")));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -480,14 +482,15 @@
     public void installFontFile_olderThanPreinstalledFont() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
-        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1");
         UpdatableFontDir dir = new UpdatableFontDir(
                 mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
                 mConfigFile);
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -500,7 +503,7 @@
         long expectedModifiedDate = 1234567890;
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
-        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1");
 
         File readonlyDir = new File(mCacheDir, "readonly");
         assertThat(readonlyDir.mkdir()).isTrue();
@@ -519,7 +522,8 @@
 
             try {
                 dir.update(
-                        Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+                        Collections.singletonList(newFontUpdateRequest("test.ttf,2",
+                                GOOD_SIGNATURE)));
             } catch (FontManagerService.SystemFontException e) {
                 assertThat(e.getErrorCode())
                         .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG);
@@ -539,7 +543,7 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs,
                 new UpdatableFontDir.FontFileParser() {
                     @Override
-                    public String getPostScriptName(File file) throws IOException {
+                    public String getCanonicalFileName(File file) throws IOException {
                         return null;
                     }
 
@@ -551,7 +555,8 @@
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -567,7 +572,7 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs,
                 new UpdatableFontDir.FontFileParser() {
                     @Override
-                    public String getPostScriptName(File file) throws IOException {
+                    public String getCanonicalFileName(File file) throws IOException {
                         throw new IOException();
                     }
 
@@ -579,7 +584,8 @@
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -615,7 +621,8 @@
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -633,11 +640,11 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE)));
         try {
             dir.update(Arrays.asList(
-                    newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
-                    newFontUpdateRequest("bar,2", "Invalid signature")));
+                    newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE),
+                    newFontUpdateRequest("bar.ttf,2", "Invalid signature")));
             fail("Batch update with invalid signature should fail");
         } catch (FontManagerService.SystemFontException e) {
             // Expected
@@ -657,7 +664,7 @@
         dir.loadFontFileMap();
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='test'>"
                         + "  <font>test.ttf</font>"
                         + "</family>")));
@@ -680,7 +687,7 @@
 
         try {
             dir.update(Arrays.asList(
-                    newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                    newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                     newAddFontFamilyRequest("<family lang='en'>"
                             + "  <font>test.ttf</font>"
                             + "</family>")));
@@ -722,7 +729,7 @@
         assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace");
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                 // Updating an existing font family.
                 newAddFontFamilyRequest("<family name='monospace'>"
                         + "  <font>test.ttf</font>"
@@ -755,7 +762,7 @@
         assertThat(firstFontFamily.getName()).isNotEmpty();
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>"
                         + "  <font>test.ttf</font>"
                         + "</family>")));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 137bd88..375704e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -16,183 +16,206 @@
 
 package com.android.server.hdmi;
 
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
 import android.annotation.NonNull;
 import android.content.Context;
-import android.util.Slog;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
 
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.XmlParser;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.datatype.DatatypeConfigurationException;
+import com.android.internal.R;
 
 /**
- * Fake class which loads default system configuration with user-configurable
+ * Fake class which stubs default system configuration with user-configurable
  * settings (useful for testing).
  */
 final class FakeHdmiCecConfig extends HdmiCecConfig {
     private static final String TAG = "FakeHdmiCecConfig";
 
-    private static final String SYSTEM_CONFIG_XML =
-            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                    + "<cec-settings>"
-                    + "  <setting name=\"send_standby_on_sleep\""
-                    + "           value-type=\"string\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value string-value=\"to_tv\" />"
-                    + "      <value string-value=\"broadcast\" />"
-                    + "      <value string-value=\"none\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value string-value=\"to_tv\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"power_state_change_on_active_source_lost\""
-                    + "           value-type=\"string\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value string-value=\"none\" />"
-                    + "      <value string-value=\"standby_now\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value string-value=\"none\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"hdmi_cec_enabled\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"hdmi_cec_version\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0x05\" />"
-                    + "      <value int-value=\"0x06\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0x05\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"system_audio_mode_muting\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"hdmi_cec_enabled\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"volume_control_enabled\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"tv_wake_on_one_touch_play\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"tv_send_standby_on_sleep\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_tv\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0x0\" />"
-                    + "      <value int-value=\"0x2\" />"
-                    + "      <value int-value=\"0x6\" />"
-                    + "      <value int-value=\"0xA\" />"
-                    + "      <value int-value=\"0xE\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0x0\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_root_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_setup_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_contents_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_top_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_media_context_sensitive_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0\" />"
-                    + "  </setting>"
-                    + "</cec-settings>";
-
-    FakeHdmiCecConfig(@NonNull Context context) {
-        super(context, new StorageAdapter(context), parseFromString(SYSTEM_CONFIG_XML), null);
+    public static Context buildContext(Context context) {
+        Context contextSpy = spy(new ContextWrapper(context));
+        doReturn(buildResources(context)).when(contextSpy).getResources();
+        return contextSpy;
     }
 
-    private static CecSettings parseFromString(@NonNull String configXml) {
-        CecSettings config = null;
-        try {
-            config = XmlParser.read(
-                    new ByteArrayInputStream(configXml.getBytes()));
-        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
-            Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
-        }
-        return config;
+    private static Resources buildResources(Context context) {
+        Resources resources = spy(context.getResources());
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecEnabled_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion14b_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion14b_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion20_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion20_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSendStandbyOnSleep_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeTv_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeTv_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeBroadcast_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeBroadcast_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeNone_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeNone_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMuting_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlMode_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTv_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvNone_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvNone_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvOne_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvOne_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvTwo_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvTwo_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvThree_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvThree_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvFour_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvFour_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
+
+        return resources;
+    }
+
+    FakeHdmiCecConfig(@NonNull Context context) {
+        super(buildContext(context), new StorageAdapter(context));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index 798cf85..c834510 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -20,6 +20,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.hdmi.HdmiControlManager;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -35,6 +37,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
+
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -61,265 +65,118 @@
     @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
     @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener;
 
+    private void setBooleanResource(int resId, boolean value) {
+        Resources resources = mContext.getResources();
+        doReturn(value).when(resources).getBoolean(resId);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getTargetContext();
-    }
-
-    @Test
-    public void getAllCecSettings_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
-    }
-
-    @Test
-    public void getAllCecSettings_Empty() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
-        assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+        mContext = FakeHdmiCecConfig.buildContext(InstrumentationRegistry.getTargetContext());
     }
 
     @Test
     public void getAllCecSettings_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
-                                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                    HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
     }
 
     @Test
-    public void getUserCecSettings_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
-    }
-
-    @Test
-    public void getUserCecSettings_Empty() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
-        assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
-    }
-
-    @Test
-    public void getUserCecSettings_OnlyMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+    public void getUserCecSettings_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getUserSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
-                                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                    HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
     }
 
     @Test
     public void getUserCecSettings_WithOverride() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>",
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>");
+        setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getUserSettings())
-                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
-    }
-
-    @Test
-    public void isStringValueType_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.isStringValueType("foo"));
+                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                    HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
     }
 
     @Test
     public void isStringValueType_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.isStringValueType("foo"));
     }
 
     @Test
     public void isStringValueType_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertTrue(hdmiCecConfig.isStringValueType(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
     }
 
     @Test
-    public void isIntValueType_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.isIntValueType("foo"));
-    }
-
-    @Test
     public void isIntValueType_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.isIntValueType("foo"));
     }
 
     @Test
     public void isIntValueType_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertTrue(hdmiCecConfig.isIntValueType(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
     }
 
     @Test
-    public void getAllowedStringValues_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getAllowedStringValues("foo"));
-    }
-
-    @Test
     public void getAllowedStringValues_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedStringValues("foo"));
     }
 
     @Test
     public void getAllowedStringValues_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedStringValues(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -327,21 +184,7 @@
 
     @Test
     public void getAllowedStringValues_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllowedStringValues(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
@@ -350,41 +193,25 @@
     }
 
     @Test
-    public void getAllowedIntValues_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getAllowedIntValues("foo"));
+    public void getAllowedStringValues_WithOverride() {
+        setBooleanResource(R.bool.config_cecPowerControlModeNone_allowed, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+        assertThat(hdmiCecConfig.getAllowedStringValues(
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+                .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+                                 HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
     @Test
     public void getAllowedIntValues_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedIntValues("foo"));
     }
 
     @Test
     public void getAllowedIntValues_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedIntValues(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -392,20 +219,7 @@
 
     @Test
     public void getAllowedIntValues_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllowedIntValues(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
                 .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
@@ -413,62 +227,24 @@
     }
 
     @Test
-    public void getAllowedIntValues_HexValues() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x00\" />"
-                + "      <value int-value=\"0x01\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x01\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+    public void getAllowedIntValues_WithOverride() {
+        setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_allowed, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllowedIntValues(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
-                .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
-                                 HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
-    }
-
-    @Test
-    public void getDefaultStringValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getDefaultStringValue("foo"));
+                .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
     }
 
     @Test
     public void getDefaultStringValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultStringValue("foo"));
     }
 
     @Test
     public void getDefaultStringValue_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -476,62 +252,46 @@
 
     @Test
     public void getDefaultStringValue_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
     }
 
     @Test
-    public void getDefaultIntValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getDefaultIntValue("foo"));
+    public void getDefaultStringValue_WithOverride() {
+        setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+        setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+        assertThat(hdmiCecConfig.getDefaultStringValue(
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+                .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+    }
+
+    @Test
+    public void getDefaultStringValue_MultipleDefaults() {
+        setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
+        assertThrows(RuntimeException.class,
+                () -> new HdmiCecConfig(mContext, mStorageAdapter));
+    }
+
+    @Test
+    public void getDefaultStringValue_NoDefault() {
+        setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+        assertThrows(RuntimeException.class,
+                () -> new HdmiCecConfig(mContext, mStorageAdapter));
     }
 
     @Test
     public void getDefaultIntValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultIntValue("foo"));
     }
 
     @Test
     public void getDefaultIntValue_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -539,81 +299,32 @@
 
     @Test
     public void getDefaultIntValue_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
                 .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
     }
 
     @Test
-    public void getDefaultIntValue_HexValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x00\" />"
-                + "      <value int-value=\"0x01\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x01\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+    public void getDefaultIntValue_WithOverride() {
+        setBooleanResource(R.bool.config_cecHdmiCecControlEnabled_default, false);
+        setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_default, true);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
-                .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
-    }
-
-    @Test
-    public void getStringValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getStringValue("foo"));
+                .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
     }
 
     @Test
     public void getStringValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getStringValue("foo"));
     }
 
     @Test
     public void getStringValue_InvalidType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -625,21 +336,7 @@
                   Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
                   HdmiControlManager.POWER_CONTROL_MODE_TV))
             .thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -652,61 +349,22 @@
                   HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE))
                 .thenReturn(
                         HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"power_state_change_on_active_source_lost\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"none\" />"
-                + "      <value string-value=\"standby_now\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"none\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
                 .isEqualTo(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
     }
 
     @Test
-    public void getIntValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getIntValue("foo"));
-    }
-
-    @Test
     public void getIntValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getIntValue("foo"));
     }
 
     @Test
     public void getIntValue_InvalidType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -718,45 +376,7 @@
                   Global.HDMI_CONTROL_ENABLED,
                   Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
             .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
-        assertThat(hdmiCecConfig.getIntValue(
-                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
-                .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
-    }
-
-    @Test
-    public void getIntValue_GlobalSetting_HexValue() {
-        when(mStorageAdapter.retrieveGlobalSetting(
-                  Global.HDMI_CONTROL_ENABLED,
-                  Integer.toHexString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
-            .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x0\" />"
-                + "      <value int-value=\"0x1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
                 .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
@@ -768,61 +388,23 @@
                   HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                   Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED)))
                 .thenReturn(Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED));
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"system_audio_mode_muting\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING))
                 .isEqualTo(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
     }
 
     @Test
-    public void setStringValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.setStringValue("foo", "bar"));
-    }
-
-    @Test
     public void setStringValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setStringValue("foo", "bar"));
     }
 
     @Test
     public void setStringValue_NotConfigurable() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        setBooleanResource(R.bool.config_cecSendStandbyOnSleep_userConfigurable, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setStringValue(
                         HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -831,21 +413,7 @@
 
     @Test
     public void setStringValue_InvalidValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setStringValue(
                         HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -854,21 +422,7 @@
 
     @Test
     public void setStringValue_GlobalSetting_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                                HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
         verify(mStorageAdapter).storeGlobalSetting(
@@ -878,20 +432,7 @@
 
     @Test
     public void setStringValue_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"power_state_change_on_active_source_lost\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"none\" />"
-                + "      <value string-value=\"standby_now\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"none\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setStringValue(
                   HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
                   HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -901,40 +442,16 @@
     }
 
     @Test
-    public void setIntValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.setIntValue("foo", 0));
-    }
-
-    @Test
     public void setIntValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setIntValue("foo", 0));
     }
 
     @Test
     public void setIntValue_NotConfigurable() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setIntValue(
                         HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -943,20 +460,7 @@
 
     @Test
     public void setIntValue_InvalidValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setIntValue(
                         HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -965,43 +469,7 @@
 
     @Test
     public void setIntValue_GlobalSetting_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
-        hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
-                                  HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
-        verify(mStorageAdapter).storeGlobalSetting(
-                  Global.HDMI_CONTROL_ENABLED,
-                  Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
-    }
-
-    @Test
-    public void setIntValue_GlobalSetting_HexValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x0\" />"
-                + "      <value int-value=\"0x1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                                   HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
         verify(mStorageAdapter).storeGlobalSetting(
@@ -1011,20 +479,7 @@
 
     @Test
     public void setIntValue_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<cec-settings>"
-                        + "  <setting name=\"system_audio_mode_muting\""
-                        + "           value-type=\"int\""
-                        + "           user-configurable=\"true\">"
-                        + "    <allowed-values>"
-                        + "      <value int-value=\"0\" />"
-                        + "      <value int-value=\"1\" />"
-                        + "    </allowed-values>"
-                        + "    <default-value int-value=\"1\" />"
-                        + "  </setting>"
-                        + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
@@ -1035,20 +490,7 @@
 
     @Test
     public void registerChangeListener_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<cec-settings>"
-                        + "  <setting name=\"system_audio_mode_muting\""
-                        + "           value-type=\"int\""
-                        + "           user-configurable=\"true\">"
-                        + "    <allowed-values>"
-                        + "      <value int-value=\"0\" />"
-                        + "      <value int-value=\"1\" />"
-                        + "    </allowed-values>"
-                        + "    <default-value int-value=\"1\" />"
-                        + "  </setting>"
-                        + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.registerChangeListener(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 mSettingChangeListener);
@@ -1061,20 +503,7 @@
 
     @Test
     public void removeChangeListener_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<cec-settings>"
-                        + "  <setting name=\"system_audio_mode_muting\""
-                        + "           value-type=\"int\""
-                        + "           user-configurable=\"true\">"
-                        + "    <allowed-values>"
-                        + "      <value int-value=\"0\" />"
-                        + "      <value int-value=\"1\" />"
-                        + "    </allowed-values>"
-                        + "    <default-value int-value=\"1\" />"
-                        + "  </setting>"
-                        + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.registerChangeListener(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 mSettingChangeListener);
@@ -1100,20 +529,7 @@
         String originalValue = Global.getString(mContext.getContentResolver(),
                 Global.HDMI_CONTROL_ENABLED);
         try {
-            HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                    mContext, mStorageAdapter,
-                    "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                            + "<cec-settings>"
-                            + "  <setting name=\"hdmi_cec_enabled\""
-                            + "           value-type=\"int\""
-                            + "           user-configurable=\"true\">"
-                            + "    <allowed-values>"
-                            + "      <value int-value=\"0\" />"
-                            + "      <value int-value=\"1\" />"
-                            + "    </allowed-values>"
-                            + "    <default-value int-value=\"1\" />"
-                            + "  </setting>"
-                            + "</cec-settings>", null);
+            HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
             hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper());
             HdmiCecConfig.SettingChangeListener latchUpdateListener =
                     new HdmiCecConfig.SettingChangeListener() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 47f3bf9..b5336e3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -45,7 +45,6 @@
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -667,7 +666,6 @@
     @Test
     public void initCecVersion_limitToMinimumSupportedVersion() {
         mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        Log.e("MARVIN", "set setting CEC");
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
@@ -679,9 +677,7 @@
 
     @Test
     public void initCecVersion_limitToAtLeast1_4() {
-        Log.e("MARVIN", "set HAL CEC to 0");
         mNativeWrapper.setCecVersion(0x0);
-        Log.e("MARVIN", "set setting CEC to 2");
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
@@ -694,7 +690,6 @@
     @Test
     public void initCecVersion_useHighestMatchingVersion() {
         mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
-        Log.e("MARVIN", "set setting CEC");
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 907cf3e..c61635c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -136,7 +136,6 @@
         mPhysicalAddress = 0x2000;
         mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
         mTestLooper.dispatchAll();
-        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
     }
 
     private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device,
@@ -147,7 +146,47 @@
     }
 
     @Test
+    public void succeedWithUnknownTvDevice() {
+        HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+                mHdmiControlService);
+        playbackDevice.init();
+        mLocalDevices.add(playbackDevice);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+
+        TestActionTimer actionTimer = new TestActionTimer();
+        TestCallback callback = new TestCallback();
+        OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+                false);
+        playbackDevice.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+                playbackDevice.mAddress, mPhysicalAddress);
+        HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+                ADDR_TV);
+        HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+                .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+        mNativeWrapper.clearResultMessages();
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+        HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+                ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+        action.processCommand(reportPowerStatusOn);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+        assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    @Test
     public void succeedAfterGettingPowerStatusOn_Cec14b() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -187,6 +226,7 @@
 
     @Test
     public void succeedAfterGettingTransientPowerStatus_Cec14b() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -236,6 +276,7 @@
 
     @Test
     public void timeOut_Cec14b() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -276,6 +317,7 @@
 
     @Test
     public void succeedIfPowerStatusOn_Cec20() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -307,6 +349,7 @@
 
     @Test
     public void succeedIfPowerStatusUnknown_Cec20() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -348,6 +391,7 @@
 
     @Test
     public void succeedIfPowerStatusStandby_Cec20() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
new file mode 100644
index 0000000..865eb7a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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.hdmi;
+
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.SystemAudioAutoInitiationAction.RETRIES_ON_TIMEOUT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/**
+ * Test for {@link SystemAudioAutoInitiationAction}.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class SystemAudioAutoInitiationActionTest {
+
+    private Context mContextSpy;
+    private HdmiControlService mHdmiControlService;
+    private FakeNativeWrapper mNativeWrapper;
+
+    private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+
+    private TestLooper mTestLooper = new TestLooper();
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mPhysicalAddress;
+
+    @Mock
+    private IPowerManager mIPowerManagerMock;
+    @Mock
+    private IThermalService mIThermalServiceMock;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+        Looper myLooper = mTestLooper.getLooper();
+        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(myLooper));
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+        mHdmiControlService = new HdmiControlService(mContextSpy) {
+            @Override
+            AudioManager getAudioManager() {
+                return new AudioManager() {
+                    @Override
+                    public void setWiredDeviceConnectionState(
+                            int type, int state, String address, String name) {
+                        // Do nothing.
+                    }
+                };
+            }
+
+            @Override
+            void wakeUp() {
+            }
+
+            @Override
+            boolean isPowerStandby() {
+                return false;
+            }
+
+            @Override
+            protected PowerManager getPowerManager() {
+                return powerManager;
+            }
+
+            @Override
+            protected void writeStringSystemProperty(String key, String value) {
+                // do nothing
+            }
+        };
+
+        mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+        mHdmiCecLocalDeviceTv.init();
+        mHdmiControlService.setIoLooper(myLooper);
+        mNativeWrapper = new FakeNativeWrapper();
+        HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+        mHdmiControlService.setCecController(hdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+        mLocalDevices.add(mHdmiCecLocalDeviceTv);
+        HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
+        hdmiPortInfos[0] =
+                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+        hdmiPortInfos[1] =
+                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
+        mNativeWrapper.setPortInfo(hdmiPortInfos);
+        mHdmiControlService.initService();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mPhysicalAddress = 0x0000;
+        mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+        mTestLooper.dispatchAll();
+        mPhysicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
+        mNativeWrapper.clearResultMessages();
+    }
+
+    private void setSystemAudioSetting(boolean on) {
+        mHdmiCecLocalDeviceTv.setSystemAudioControlFeatureEnabled(on);
+    }
+
+    private void setTvHasSystemAudioChangeAction() {
+        mHdmiCecLocalDeviceTv.addAndStartAction(
+                new SystemAudioActionFromTv(mHdmiCecLocalDeviceTv, Constants.ADDR_AUDIO_SYSTEM,
+                        true, null));
+    }
+
+    @Test
+    public void testReceiveSystemAudioMode_systemAudioOn() {
+        // Record that previous system audio mode is on.
+        setSystemAudioSetting(true);
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+        HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+        mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiControlService.isSystemAudioActivated()).isTrue();
+    }
+
+    @Test
+    public void testReceiveSystemAudioMode_systemAudioOnAndImpossibleToChangeSystemAudio() {
+        // Turn on system audio.
+        setSystemAudioSetting(true);
+        // Impossible to change system audio mode while SystemAudioActionFromTv is in progress.
+        setTvHasSystemAudioChangeAction();
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+        HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+        mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiControlService.isSystemAudioActivated()).isFalse();
+    }
+
+    @Test
+    public void testReceiveSystemAudioMode_systemAudioOnAndResponseOff() {
+        // Record that previous system audio mode is on.
+        setSystemAudioSetting(true);
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+        HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
+        mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
+        SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
+                SystemAudioActionFromTv.class).get(0);
+        assertThat(resultingAction.mTargetAudioStatus).isTrue();
+    }
+
+    @Test
+    public void testReceiveSystemAudioMode_settingOffAndResponseOn() {
+        // Turn off system audio.
+        setSystemAudioSetting(false);
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+        HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+        mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
+        SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
+                SystemAudioActionFromTv.class).get(0);
+        assertThat(resultingAction.mTargetAudioStatus).isFalse();
+    }
+
+    @Test
+    public void testReceiveSystemAudioMode_settingOffAndResponseOff() {
+        // Turn off system audio.
+        setSystemAudioSetting(false);
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+        HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
+        mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isEmpty();
+        assertThat(mHdmiControlService.isSystemAudioActivated()).isFalse();
+    }
+
+    @Test
+    public void testTimeout_systemAudioOn_retries() {
+        // Turn on system audio.
+        setSystemAudioSetting(true);
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+        mNativeWrapper.clearResultMessages();
+
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Retry sends <Give System Audio Mode Status> again
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+    }
+
+    @Test
+    public void testTimeout_systemAudioOn_allRetriesFail() {
+        boolean targetStatus = true;
+        // Turn on system audio.
+        setSystemAudioSetting(targetStatus);
+
+        HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
+                ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveSystemAudioModeStatus =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+                        mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+
+        for (int i = 0; i < RETRIES_ON_TIMEOUT; i++) {
+            mNativeWrapper.clearResultMessages();
+
+            // Target device doesn't respond within timeout
+            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+            mTestLooper.dispatchAll();
+
+            // Retry sends <Give System Audio Mode Status> again
+            assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
+        }
+
+        // Target device doesn't respond within timeouts
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
+        SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
+                SystemAudioActionFromTv.class).get(0);
+        assertThat(resultingAction.mTargetAudioStatus).isEqualTo(targetStatus);
+    }
+}
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 b51f4df..91342ce 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -109,7 +110,8 @@
     public interface MockableRebootEscrowInjected {
         int getBootCount();
 
-        void reportMetric(boolean success);
+        void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+                int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
     }
 
     static class MockInjector extends RebootEscrowManager.Injector {
@@ -119,6 +121,7 @@
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
+        private final boolean mServerBased;
 
         MockInjector(Context context, UserManager userManager,
                 IRebootEscrow rebootEscrow,
@@ -128,6 +131,7 @@
             super(context, storage);
             mRebootEscrow = rebootEscrow;
             mServiceConnection = null;
+            mServerBased = false;
             RebootEscrowProviderHalImpl.Injector halInjector =
                     new RebootEscrowProviderHalImpl.Injector() {
                         @Override
@@ -149,6 +153,7 @@
             super(context, storage);
             mServiceConnection = serviceConnection;
             mRebootEscrow = null;
+            mServerBased = true;
             RebootEscrowProviderServerBasedImpl.Injector injector =
                     new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
             mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
@@ -168,6 +173,11 @@
         }
 
         @Override
+        public boolean serverBasedResumeOnReboot() {
+            return mServerBased;
+        }
+
+        @Override
         public RebootEscrowProviderInterface getRebootEscrowProvider() {
             return mRebootEscrowProvider;
         }
@@ -195,8 +205,11 @@
         }
 
         @Override
-        public void reportMetric(boolean success) {
-            mInjected.reportMetric(success);
+        public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+                int escrowDurationInSeconds, int vbmetaDigestStatus,
+                int durationSinceBootComplete) {
+            mInjected.reportMetric(success, errorCode, serviceType, attemptCount,
+                    escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete);
         }
     }
 
@@ -418,7 +431,9 @@
 
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */,
+                anyInt(), anyInt(), anyInt());
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
         mService.loadRebootEscrowDataIfAvailable(null);
@@ -451,7 +466,9 @@
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */,
+                anyInt(), anyInt(), anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenAnswer(invocation -> invocation.getArgument(0));
@@ -485,7 +502,8 @@
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenThrow(new IOException())
@@ -528,7 +546,8 @@
 
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
-        verify(mInjected, never()).reportMetric(anyBoolean());
+        verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -554,7 +573,8 @@
         when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
 
         mService.loadRebootEscrowDataIfAvailable(null);
-        verify(mInjected, never()).reportMetric(anyBoolean());
+        verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -588,7 +608,8 @@
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
         mService.loadRebootEscrowDataIfAvailable(null);
-        verify(mInjected).reportMetric(eq(true));
+        verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */,
+                eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -615,7 +636,9 @@
 
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                anyInt() /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */,
+                anyInt(), anyInt(), anyInt());
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index dd3054f..3fd2c97 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -32,7 +32,6 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.security.GateKeeper;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
@@ -59,6 +58,7 @@
 import java.util.List;
 
 import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -575,7 +575,7 @@
         return (KeyProtection) mProtectionParameterCaptor.getValue();
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -584,7 +584,7 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 
     class PlatformKeyManagerTestable extends PlatformKeyManager {
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9a52643..9f428c7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.content.pm.parsing.ParsingPackage;
@@ -141,6 +142,10 @@
         return pkg(packageName).addReceiver(receiver);
     }
 
+    private static ParsingPackage pkgWithSharedLibrary(String packageName, String libName) {
+        return pkg(packageName).addLibraryName(libName);
+    }
+
     private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) {
         ParsedActivity activity = new ParsedActivity();
         activity.setPackageName(packageName);
@@ -413,6 +418,118 @@
     }
 
     @Test
+    public void testNoUsesLibrary_Filters() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
+    public void testUsesLibrary_DoesntFilter() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package").addUsesLibrary("com.some.shared_library"),
+                DUMMY_CALLING_APPID);
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
+    public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package").addUsesOptionalLibrary("com.some.shared_library"),
+                DUMMY_CALLING_APPID);
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
+    public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package_a").setSharedUserId("com.some.uid"),
+                DUMMY_CALLING_APPID);
+        simulateAddPackage(appsFilter, pkg("com.some.other.package_b")
+                .setSharedUserId("com.some.uid").addUsesLibrary("com.some.shared_library"),
+                DUMMY_CALLING_APPID);
+
+        // Although package_a doesn't use library, it should be granted visibility. It's because
+        // package_a shares userId with package_b, and package_b uses that shared library.
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
     public void testForceQueryable_SystemDoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
new file mode 100644
index 0000000..3ab3448
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static org.mockito.Mockito.inOrder;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Unit tests for {@link com.android.server.pm.dex.ArtStatsLogUtils}.
+ *
+ * Run with "atest ArtStatsLogUtilsTest".
+ */
+@RunWith(JUnit4.class)
+public final class ArtStatsLogUtilsTest {
+    private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName();
+    private static final String COMPILER_FILTER = "space-profile";
+    private static final String PROFILE_DEX_METADATA = "primary.prof";
+    private static final String VDEX_DEX_METADATA = "primary.vdex";
+    private static final byte[] DEX_CONTENT = "dexData".getBytes();
+    private static final int COMPILATION_REASON = 1;
+    private static final int RESULT_CODE = 222;
+    private static final int UID = 111;
+    private static final long COMPILE_TIME = 333L;
+    private static final long SESSION_ID = 444L;
+
+    @Mock
+    ArtStatsLogger mockLogger;
+
+    private static Path TEST_DIR;
+    private static Path DEX;
+    private static Path NON_DEX;
+
+    @BeforeClass
+    public static void setUpAll() throws IOException {
+        TEST_DIR = Files.createTempDirectory(null);
+        DEX = Files.createFile(TEST_DIR.resolve("classes.dex"));
+        NON_DEX = Files.createFile(TEST_DIR.resolve("test.dex"));
+        Files.write(DEX, DEX_CONTENT);
+        Files.write(NON_DEX, "empty".getBytes());
+    }
+
+    @AfterClass
+    public static void tearnDownAll() {
+        deleteSliently(DEX);
+        deleteSliently(NON_DEX);
+        deleteSliently(TEST_DIR);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testProfileAndVdexDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA, VDEX_DEX_METADATA);
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testProfileOnlyDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA);
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testVdexOnlyDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata(VDEX_DEX_METADATA);
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testNoneDexMetadata() throws IOException {
+        // Setup
+        Path apk = null;
+        try {
+            apk = zipFiles(".apk", DEX, NON_DEX);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    /*dexMetadataPath=*/ null,
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA);
+        } finally {
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testUnKnownDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata("unknown");
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    private void verifyWrites(int dexMetadataType) {
+        InOrder inorder = inOrder(mockLogger);
+        inorder.verify(mockLogger).write(
+                SESSION_ID, UID,
+                COMPILATION_REASON,
+                COMPILER_FILTER,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+                RESULT_CODE,
+                dexMetadataType);
+        inorder.verify(mockLogger).write(
+                SESSION_ID,
+                UID,
+                COMPILATION_REASON,
+                COMPILER_FILTER,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+                DEX_CONTENT.length,
+                dexMetadataType);
+        inorder.verify(mockLogger).write(
+                SESSION_ID,
+                UID,
+                COMPILATION_REASON,
+                COMPILER_FILTER,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+                COMPILE_TIME,
+                dexMetadataType);
+    }
+
+    private Path zipFiles(String suffix, Path... files) throws IOException {
+        Path zipFile = Files.createTempFile(null, suffix);
+        try (final OutputStream os = Files.newOutputStream(zipFile)) {
+            try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+                for (Path file : files) {
+                    ZipEntry zipEntry = new ZipEntry(file.getFileName().toString());
+                    zos.putNextEntry(zipEntry);
+                    zos.write(Files.readAllBytes(file));
+                    zos.closeEntry();
+                }
+            }
+        }
+        return zipFile;
+    }
+
+    private Path createDexMetadata(String... entryNames) throws IOException {
+        Path zipFile = Files.createTempFile(null, ".dm");
+        try (final OutputStream os = Files.newOutputStream(zipFile)) {
+            try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+                for (String entryName : entryNames) {
+                    ZipEntry zipEntry = new ZipEntry(entryName);
+                    zos.putNextEntry(zipEntry);
+                    zos.write(entryName.getBytes());
+                    zos.closeEntry();
+                }
+            }
+        }
+        return zipFile;
+    }
+
+    private static void deleteSliently(Path file) {
+        if (file != null) {
+            try {
+                Files.deleteIfExists(file);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
index 22c38c1..fa2123c 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -31,6 +31,7 @@
 import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.rotationresolver.RotationResolverInternal;
+import android.service.rotationresolver.RotationResolutionRequest;
 import android.view.Surface;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -58,6 +59,7 @@
     private Context mContext;
     private CancellationSignal mCancellationSignal;
     private RotationResolverManagerPerUserService mService;
+    private RotationResolutionRequest mRequest;
 
     @Before
     public void setUp() throws RemoteException {
@@ -79,9 +81,10 @@
 
         mCancellationSignal = new CancellationSignal();
 
+        mRequest = new RotationResolutionRequest("", Surface.ROTATION_0, Surface.ROTATION_0,
+                true, 1000L);
         this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
-                mMockCallbackInternal, Surface.ROTATION_0, Surface.ROTATION_0, "", 1000L,
-                mCancellationSignal);
+                mMockCallbackInternal, mRequest, mCancellationSignal);
 
         this.mService.getMaster().mIsServiceEnabled = true;
 
@@ -99,8 +102,7 @@
         RotationResolverInternal.RotationResolverCallbackInternal callbackInternal =
                 Mockito.mock(RotationResolverInternal.RotationResolverCallbackInternal.class);
 
-        mService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
-                "", 1000L, mCancellationSignal);
+        mService.resolveRotationLocked(callbackInternal, mRequest, mCancellationSignal);
         verify(callbackInternal).onSuccess(anyInt());
     }
 
@@ -110,8 +112,7 @@
                 Mockito.mock(RotationResolverInternal.RotationResolverCallbackInternal.class);
 
         final CancellationSignal cancellationSignal = new CancellationSignal();
-        mService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
-                "", 1000L, cancellationSignal);
+        mService.resolveRotationLocked(callbackInternal, mRequest, cancellationSignal);
         cancellationSignal.cancel();
     }
 
@@ -132,7 +133,7 @@
 
         @Override
         public void resolveRotationLocked(RotationRequest request) {
-            request.mCallbackInternal.onSuccess(request.mProposedRotation);
+            request.mCallbackInternal.onSuccess(request.mRemoteRequest.getProposedRotation());
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 5f86d28..bbf11fd 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -209,7 +209,7 @@
     @Test(expected = SecurityException.class)
     public void testSuggestExternalTime_withoutPermission() {
         doThrow(new SecurityException("Mock"))
-                .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+                .when(mMockContext).enforceCallingPermission(anyString(), any());
         ExternalTimeSuggestion externalTimeSuggestion = createExternalTimeSuggestion();
 
         try {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 5d27552..0036982 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -46,7 +46,7 @@
     public void test_unrestricted() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionFeatureSupported(true)
+                .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
@@ -108,7 +108,7 @@
     public void test_restricted() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(false)
-                .setAutoDetectionFeatureSupported(true)
+                .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
@@ -170,7 +170,7 @@
     public void test_autoDetectNotSupported() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionFeatureSupported(false)
+                .setTelephonyDetectionFeatureSupported(false)
                 .setGeoDetectionFeatureSupported(false)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
@@ -232,7 +232,7 @@
     public void test_geoDetectNotSupported() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionFeatureSupported(true)
+                .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(false)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index bad380a..51f627a 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -118,6 +118,11 @@
     }
 
     @Override
+    public MetricsTimeZoneDetectorState generateMetricsState() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void addDumpable(Dumpable dumpable) {
         mDumpables.add(dumpable);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
new file mode 100644
index 0000000..af954d5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class OrdinalGeneratorTest {
+
+    @Test
+    public void testOrdinal() {
+        OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+        int oneOrd = ordinalGenerator.ordinal("One");
+        int twoOrd = ordinalGenerator.ordinal("Two");
+        assertNotEquals(oneOrd, twoOrd);
+
+        assertEquals(oneOrd, ordinalGenerator.ordinal("One"));
+        assertEquals(twoOrd, ordinalGenerator.ordinal("Two"));
+
+        int threeOrd = ordinalGenerator.ordinal("Three");
+        assertNotEquals(oneOrd, threeOrd);
+        assertNotEquals(twoOrd, threeOrd);
+    }
+
+    @Test
+    public void testOrdinals() {
+        OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+        int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two"));
+        int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three"));
+        assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("One"));
+        assertEquals(oneTwoOrds[1], ordinalGenerator.ordinal("Two"));
+        assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("Two"));
+        assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("Three"));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 14e0bbd..8af2c4d 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -364,7 +364,7 @@
         // the tests.
         final boolean geoDetectionEnabled = autoDetectionEnabled;
         return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
-                .setAutoDetectionFeatureSupported(true)
+                .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionEnabled(autoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index f1f8b2f..f91ce87 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -90,7 +90,7 @@
     private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
-                    .setAutoDetectionFeatureSupported(true)
+                    .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
@@ -100,7 +100,7 @@
     private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
-                    .setAutoDetectionFeatureSupported(true)
+                    .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -110,17 +110,17 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
-                    .setAutoDetectionFeatureSupported(false)
+                    .setTelephonyDetectionFeatureSupported(false)
                     .setGeoDetectionFeatureSupported(false)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
-    private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
+    private static final ConfigurationInternal CONFIG_INT_TELEPHONY_SUPPORTED_GEO_NOT_SUPPORTED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
-                    .setAutoDetectionFeatureSupported(true)
+                    .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(false)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -130,7 +130,7 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
-                    .setAutoDetectionFeatureSupported(true)
+                    .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
@@ -139,7 +139,7 @@
 
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
-                    .setAutoDetectionFeatureSupported(true)
+                    .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
@@ -149,7 +149,7 @@
 
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
             new ConfigurationInternal.Builder(USER_ID)
-                    .setAutoDetectionFeatureSupported(true)
+                    .setTelephonyDetectionFeatureSupported(true)
                     .setGeoDetectionFeatureSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
@@ -266,7 +266,8 @@
 
     @Test
     public void testUpdateConfiguration_autoDetectSupportedGeoNotSupported() {
-        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED);
+        Script script = new Script().initializeConfig(
+                CONFIG_INT_TELEPHONY_SUPPORTED_GEO_NOT_SUPPORTED);
 
         // Update the configuration with auto detection disabled.
         script.simulateUpdateConfiguration(
@@ -274,7 +275,7 @@
 
         // The settings should have been changed and the StrategyListener onChange() called.
         ConfigurationInternal expectedConfig =
-                new ConfigurationInternal.Builder(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED)
+                new ConfigurationInternal.Builder(CONFIG_INT_TELEPHONY_SUPPORTED_GEO_NOT_SUPPORTED)
                         .setAutoDetectionEnabled(false)
                         .build();
         script.verifyConfigurationChangedAndReset(expectedConfig);
@@ -675,6 +676,8 @@
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
                 .verifyTimeZoneNotChanged();
+
+        assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
     }
 
     @Test
@@ -687,6 +690,8 @@
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
                 .verifyTimeZoneNotChanged();
+
+        assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
     }
 
     @Test
@@ -700,6 +705,8 @@
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, manualSuggestion, true /* expectedResult */)
             .verifyTimeZoneChangedAndReset(manualSuggestion);
+
+        assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
     }
 
     @Test
@@ -713,6 +720,8 @@
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, manualSuggestion, false /* expectedResult */)
                 .verifyTimeZoneNotChanged();
+
+        assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion());
     }
 
     @Test
@@ -726,6 +735,8 @@
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, manualSuggestion, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(manualSuggestion);
+
+        assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
     }
 
     @Test
@@ -914,6 +925,106 @@
         assertTrue(dumpCalled.get());
     }
 
+    @Test
+    public void testGenerateMetricsState() {
+        ConfigurationInternal expectedInternalConfig = CONFIG_INT_AUTO_DISABLED_GEO_DISABLED;
+        String expectedDeviceTimeZoneId = "InitialZoneId";
+
+        Script script = new Script()
+                .initializeConfig(expectedInternalConfig)
+                .initializeTimeZoneSetting(expectedDeviceTimeZoneId);
+
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, null, null,
+                null, MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+        // Make sure the manual suggestion is recorded.
+        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1");
+        script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion,
+                true /* expectedResult */)
+                .verifyTimeZoneChangedAndReset(manualSuggestion);
+        expectedDeviceTimeZoneId = manualSuggestion.getZoneId();
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, null, null,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+        // With time zone auto detection off, telephony suggestions will be recorded, but geo
+        // suggestions won't out of an abundance of caution around respecting user privacy when
+        // geo detection is off.
+        TelephonyTimeZoneSuggestion telephonySuggestion =
+                createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+                        QUALITY_SINGLE_ZONE, "Zone2");
+        GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion =
+                createGeoLocationSuggestion(Arrays.asList("Zone3", "Zone2"));
+        script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+                .verifyTimeZoneNotChanged()
+                .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+                .verifyTimeZoneNotChanged();
+
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+        // Update the config and confirm that the config metrics state updates also.
+        TimeZoneConfiguration configUpdate =
+                createConfig(true /* autoDetection */, true /* geoDetection */);
+        expectedInternalConfig = new ConfigurationInternal.Builder(expectedInternalConfig)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
+                .verifyConfigurationChangedAndReset(expectedInternalConfig)
+                .verifyTimeZoneNotChanged();
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, telephonySuggestion, null  /* expectedGeoSuggestion */,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+
+        // Now simulate a geo suggestion and confirm it is used and reported in the metrics too.
+        expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
+        script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+                .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+    }
+
+    /**
+     * Asserts that the information returned by {@link
+     * TimeZoneDetectorStrategy#generateMetricsState()} matches expectations.
+     */
+    private void assertMetricsState(
+            ConfigurationInternal expectedInternalConfig,
+            String expectedDeviceTimeZoneId, ManualTimeZoneSuggestion expectedManualSuggestion,
+            TelephonyTimeZoneSuggestion expectedTelephonySuggestion,
+            GeolocationTimeZoneSuggestion expectedGeolocationTimeZoneSuggestion,
+            int expectedDetectionMode) {
+
+        MetricsTimeZoneDetectorState actualState = mTimeZoneDetectorStrategy.generateMetricsState();
+
+        // Check the various feature state values are what we expect.
+        assertFeatureStateMatchesConfig(expectedInternalConfig, actualState, expectedDetectionMode);
+
+        OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+        MetricsTimeZoneDetectorState expectedState =
+                MetricsTimeZoneDetectorState.create(
+                        tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
+                        expectedManualSuggestion, expectedTelephonySuggestion,
+                        expectedGeolocationTimeZoneSuggestion);
+        // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons.
+        assertEquals(expectedState, actualState);
+    }
+
+    private static void assertFeatureStateMatchesConfig(ConfigurationInternal config,
+            MetricsTimeZoneDetectorState actualState, int expectedDetectionMode) {
+        assertEquals(config.isTelephonyDetectionSupported(),
+                actualState.isTelephonyDetectionSupported());
+        assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported());
+        assertEquals(config.getAutoDetectionEnabledSetting(),
+                actualState.getAutoDetectionEnabledSetting());
+        assertEquals(config.getGeoDetectionEnabledSetting(),
+                actualState.getGeoDetectionEnabledSetting());
+        assertEquals(expectedDetectionMode, actualState.getDetectionMode());
+    }
+
     private static ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
         return new ManualTimeZoneSuggestion(zoneId);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 4284240..5a100a2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -72,18 +72,25 @@
     private TestCallback mTestCallback;
     private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
     private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
+    private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;
 
     @Before
     public void setUp() {
         // For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
         // runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
         // will never get a chance to execute.
+        LocationTimeZoneProvider.ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> {
+            // Stubbed.
+        };
         mTestThreadingDomain = new TestThreadingDomain();
         mTestCallback = new TestCallback(mTestThreadingDomain);
-        mTestPrimaryLocationTimeZoneProvider =
-                new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary");
-        mTestSecondaryLocationTimeZoneProvider =
-                new TestLocationTimeZoneProvider(mTestThreadingDomain, "secondary");
+        mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
+        mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+                stubbedProviderMetricsLogger, mTestThreadingDomain, "primary",
+                mTimeZoneAvailabilityChecker);
+        mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+                stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary",
+                mTimeZoneAvailabilityChecker);
     }
 
     @Test
@@ -1177,8 +1184,11 @@
         /**
          * Creates the instance.
          */
-        TestLocationTimeZoneProvider(ThreadingDomain threadingDomain, String providerName) {
-            super(threadingDomain, providerName);
+        TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
+                ThreadingDomain threadingDomain, String providerName,
+                TimeZoneIdValidator timeZoneIdValidator) {
+            super(providerMetricsLogger, threadingDomain, providerName,
+                    timeZoneIdValidator);
         }
 
         public void setFailDuringInitialization(boolean failInitialization) {
@@ -1311,4 +1321,14 @@
             mTestProviderState.commitLatest();
         }
     }
+
+    private static final class FakeTimeZoneIdValidator
+            implements LocationTimeZoneProvider.TimeZoneIdValidator {
+
+        @Override
+        public boolean isValid(@NonNull String timeZoneId) {
+            return true;
+        }
+
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index 095c868..d13a04e 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -32,6 +32,8 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import static java.util.Arrays.asList;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
@@ -50,7 +52,10 @@
 
 import java.time.Duration;
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -62,27 +67,31 @@
     private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 123456789L;
 
     private TestThreadingDomain mTestThreadingDomain;
-
     private TestProviderListener mProviderListener;
+    private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;
 
     @Before
     public void setUp() {
         mTestThreadingDomain = new TestThreadingDomain();
         mProviderListener = new TestProviderListener();
+        mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
     }
 
     @Test
     public void lifecycle() {
         String providerName = "arbitrary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+        RecordingProviderMetricsLogger providerMetricsLogger = new RecordingProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
+        mTimeZoneAvailabilityChecker.validIds("Europe/London");
 
         // initialize()
         provider.initialize(mProviderListener);
         provider.assertOnInitializeCalled();
 
-        ProviderState currentState = provider.getCurrentState();
-        assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+        ProviderState currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
         assertNull(currentState.currentUserConfiguration);
         assertSame(provider, currentState.provider);
         mTestThreadingDomain.assertQueueEmpty();
@@ -96,9 +105,9 @@
 
         provider.assertOnStartCalled(arbitraryInitializationTimeout);
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING);
         assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
         assertEquals(config, currentState.currentUserConfiguration);
         assertNull(currentState.event);
         // The initialization timeout should be queued.
@@ -120,9 +129,9 @@
         TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
         provider.simulateProviderEventReceived(event);
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN);
         assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_CERTAIN, currentState.stateEnum);
         assertEquals(event, currentState.event);
         assertEquals(config, currentState.currentUserConfiguration);
         mTestThreadingDomain.assertQueueEmpty();
@@ -132,9 +141,9 @@
         event = TimeZoneProviderEvent.createUncertainEvent();
         provider.simulateProviderEventReceived(event);
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN);
         assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, currentState.stateEnum);
         assertEquals(event, currentState.event);
         assertEquals(config, currentState.currentUserConfiguration);
         mTestThreadingDomain.assertQueueEmpty();
@@ -144,7 +153,8 @@
         provider.stopUpdates();
         provider.assertOnStopUpdatesCalled();
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
         assertSame(provider, currentState.provider);
         assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
         assertNull(currentState.event);
@@ -162,8 +172,10 @@
     @Test
     public void defaultHandleTestCommandImpl() {
         String providerName = "primary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+        StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
 
         TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
         AtomicReference<Bundle> resultReference = new AtomicReference<>();
@@ -179,9 +191,12 @@
     @Test
     public void stateRecording() {
         String providerName = "primary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+        StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
         provider.setStateChangeRecordingEnabled(true);
+        mTimeZoneAvailabilityChecker.validIds("Europe/London");
 
         // initialize()
         provider.initialize(mProviderListener);
@@ -218,6 +233,33 @@
         provider.assertLatestRecordedState(PROVIDER_STATE_DESTROYED);
     }
 
+    @Test
+    public void considerSuggestionWithInvalidTimeZoneIdsAsUncertain() {
+        String providerName = "primary";
+        StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
+        provider.setStateChangeRecordingEnabled(true);
+        provider.initialize(mProviderListener);
+
+        ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
+        Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
+        Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
+        provider.startUpdates(config, arbitraryInitializationTimeout,
+                arbitraryInitializationTimeoutFuzz);
+
+        List<String> invalidTimeZoneIds = asList("Atlantic/Atlantis");
+        TimeZoneProviderSuggestion invalidIdSuggestion = new TimeZoneProviderSuggestion.Builder()
+                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .setTimeZoneIds(invalidTimeZoneIds)
+                .build();
+        TimeZoneProviderEvent event =
+                TimeZoneProviderEvent.createSuggestionEvent(invalidIdSuggestion);
+        provider.simulateProviderEventReceived(event);
+        provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN);
+    }
+
     /** A test stand-in for the real {@link LocationTimeZoneProviderController}'s listener. */
     private static class TestProviderListener implements ProviderListener {
 
@@ -241,6 +283,20 @@
         }
     }
 
+    /**
+     * Returns the provider's state after asserting that the current state matches what is expected.
+     * This also asserts that the metrics logger was informed of the state change.
+     */
+    private static ProviderState assertAndReturnProviderState(
+            TestLocationTimeZoneProvider provider,
+            RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum) {
+        ProviderState currentState = provider.getCurrentState();
+        assertEquals(expectedStateEnum, currentState.stateEnum);
+        providerMetricsLogger.assertChangeLoggedAndRemove(expectedStateEnum);
+        providerMetricsLogger.assertNoMoreLogEntries();
+        return currentState;
+    }
+
     private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
 
         private boolean mOnInitializeCalled;
@@ -250,9 +306,11 @@
         private boolean mOnStopUpdatesCalled;
 
         /** Creates the instance. */
-        TestLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
-                @NonNull String providerName) {
-            super(threadingDomain, providerName);
+        TestLocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+                @NonNull ThreadingDomain threadingDomain,
+                @NonNull String providerName,
+                @NonNull TimeZoneIdValidator timeZoneIdValidator) {
+            super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator);
         }
 
         @Override
@@ -308,4 +366,49 @@
                     recordedStates.get(recordedStates.size() - 1).stateEnum);
         }
     }
+
+    private static final class FakeTimeZoneIdValidator
+            implements LocationTimeZoneProvider.TimeZoneIdValidator {
+        private final Set<String> mValidTimeZoneIds = new HashSet<>();
+
+        @Override
+        public boolean isValid(@NonNull String timeZoneId) {
+            return mValidTimeZoneIds.contains(timeZoneId);
+        }
+
+        public void validIds(String... timeZoneIdss) {
+            mValidTimeZoneIds.addAll(asList(timeZoneIdss));
+        }
+    }
+
+    private static class StubbedProviderMetricsLogger implements
+            LocationTimeZoneProvider.ProviderMetricsLogger {
+
+        @Override
+        public void onProviderStateChanged(int stateEnum) {
+            // Stubbed
+        }
+    }
+
+    private static class RecordingProviderMetricsLogger implements
+            LocationTimeZoneProvider.ProviderMetricsLogger {
+
+        private LinkedList<Integer> mStates = new LinkedList<>();
+
+        @Override
+        public void onProviderStateChanged(int stateEnum) {
+            mStates.add(stateEnum);
+        }
+
+        public void assertChangeLoggedAndRemove(int expectedLoggedState) {
+            assertEquals("expected loggedState=" + expectedLoggedState
+                    + " but states logged were=" + mStates,
+                    (Integer) expectedLoggedState, mStates.peekFirst());
+            mStates.removeFirst();
+        }
+
+        public void assertNoMoreLogEntries() {
+            assertTrue(mStates.isEmpty());
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index 8280cdc..16ac1d6 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -44,7 +44,7 @@
             @UserIdInt int userId, boolean geoDetectionEnabled) {
         return new ConfigurationInternal.Builder(userId)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionFeatureSupported(true)
+                .setTelephonyDetectionFeatureSupported(true)
                 .setGeoDetectionFeatureSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java
new file mode 100644
index 0000000..5561b2c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.TimeZone;
+
+@Presubmit
+public class ZoneInfoDbTimeZoneIdValidatorTest {
+    private final LocationTimeZoneProvider.TimeZoneIdValidator mTzChecker =
+            new ZoneInfoDbTimeZoneIdValidator();
+
+    @Test
+    public void timeZoneIdsFromZoneInfoDbAreValid() {
+        for (String timeZone : TimeZone.getAvailableIDs()) {
+            assertWithMessage("Time zone %s should be supported", timeZone)
+                    .that(mTzChecker.isValid(timeZone)).isTrue();
+        }
+    }
+
+    @Test
+    public void nonExistingZones_areNotSupported() {
+        List<String> nonExistingTimeZones = Arrays.asList(
+                "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30"
+        );
+
+        for (String timeZone : nonExistingTimeZones) {
+            assertWithMessage(timeZone + " is not a valid time zone")
+                    .that(mTzChecker.isValid(timeZone))
+                    .isFalse();
+        }
+    }
+}
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 2a3c2c4..b54b696 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -49,6 +49,8 @@
     private int mCapabilities;
     private int[] mSupportedEffects;
     private int[] mSupportedPrimitives;
+    private float mResonantFrequency;
+    private float mQFactor;
 
     private final class FakeNativeWrapper extends VibratorController.NativeWrapper {
         public int vibratorId;
@@ -89,6 +91,14 @@
             return mSupportedPrimitives;
         }
 
+        public float getResonantFrequency() {
+            return mResonantFrequency;
+        }
+
+        public float getQFactor() {
+            return mQFactor;
+        }
+
         public long perform(long effect, long strength, long vibrationId) {
             if (mSupportedEffects == null
                     || Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
@@ -198,6 +208,16 @@
         mSupportedPrimitives = primitives;
     }
 
+    /** Set the resonant frequency of the fake vibrator hardware. */
+    public void setResonantFrequency(float resonantFrequency) {
+        mResonantFrequency = resonantFrequency;
+    }
+
+    /** Set the Q factor of the fake vibrator hardware. */
+    public void setQFactor(float qFactor) {
+        mQFactor = qFactor;
+    }
+
     /**
      * Return the amplitudes set by this controller, including zeroes for each time the vibrator was
      * turned off.
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 1b7e1ca..7d5eec0 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -107,7 +107,8 @@
         waitForCompletion(thread);
 
         verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
-        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED));
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
+                eq(Vibration.Status.IGNORED_UNSUPPORTED));
     }
 
     @Test
@@ -121,7 +122,8 @@
         waitForCompletion(thread);
 
         verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
-        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.IGNORED));
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
+                eq(Vibration.Status.IGNORED_UNSUPPORTED));
     }
 
     @Test
@@ -206,8 +208,8 @@
         thread.cancel();
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
-        verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
+        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
         verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index a28d18f..ce6639c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -266,6 +266,8 @@
         vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
         vibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
+        vibrator.setResonantFrequency(123.f);
+        vibrator.setQFactor(Float.NaN);
         VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
 
         assertNotNull(info);
@@ -279,6 +281,8 @@
                 info.isEffectSupported(VibrationEffect.EFFECT_TICK));
         assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
         assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
+        assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+        assertTrue(Float.isNaN(info.getQFactor()));
     }
 
     @Test
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 8789992..799ec53 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -17,9 +17,13 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.servicestests.apps.simpleservicetestapp">
 
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
     <application>
         <service android:name=".SimpleService"
                  android:exported="true" />
+        <service android:name=".SimpleFgService"
+                 android:exported="true" />
     </application>
 
 </manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
new file mode 100644
index 0000000..ccfc0b7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.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 com.android.servicestests.apps.simpleservicetestapp;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.R;
+
+public class SimpleFgService extends Service {
+    private static final String TAG = SimpleFgService.class.getSimpleName();
+    private static final String NOTIFICATION_CHANNEL_ID = TAG;
+    private static final int NOTIFICATION_ID = 1;
+
+    private static final int MSG_INIT = 0;
+    private static final int MSG_DONE = 1;
+    private static final int MSG_START_FOREGROUND = 2;
+    private static final int MSG_STOP_FOREGROUND = 3;
+
+    private static final String ACTION_FGS_STATS_TEST =
+            "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+    private static final String EXTRA_MESSENGER = "extra_messenger";
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_START_FOREGROUND: {
+                    Log.i(TAG, "startForeground");
+                    startForeground(NOTIFICATION_ID, mNotification);
+                    sendRemoteMessage(MSG_DONE, 0, 0, null);
+                } break;
+                case MSG_STOP_FOREGROUND: {
+                    Log.i(TAG, "stopForeground");
+                    stopForeground(true);
+                    sendRemoteMessage(MSG_DONE, 0, 0, null);
+                } break;
+            }
+        }
+    };
+    private final Messenger mMessenger = new Messenger(mHandler);
+
+    private Notification mNotification;
+    private Messenger mRemoteMessenger;
+
+    @Override
+    public void onCreate() {
+        Log.i(TAG, "onCreate");
+        final NotificationManager nm = getSystemService(NotificationManager.class);
+        nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW));
+        mNotification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+                .setContentTitle(TAG)
+                .setSmallIcon(R.drawable.ic_info)
+                .build();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i(TAG, "onStartCommand");
+        startForeground(NOTIFICATION_ID, mNotification);
+        if (ACTION_FGS_STATS_TEST.equals(intent.getAction())) {
+            mRemoteMessenger = new Messenger(intent.getExtras().getBinder(EXTRA_MESSENGER));
+            sendRemoteMessage(MSG_INIT, 0, 0, mMessenger);
+        }
+        return START_NOT_STICKY;
+    }
+
+    private void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+        final Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        try {
+            mRemoteMessenger.send(msg);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.i(TAG, "onDestroy");
+        mNotification = null;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 5182b3b..b921838 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -243,10 +243,18 @@
         final UserHandle user = getParentUser(context);
         List<String> roleHolders = callWithShellPermissionIdentity(
                 () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
-        if (roleHolders.size() == 1) {
+        int size = roleHolders.size();
+        if (size == 1) {
             return roleHolders.get(0);
         }
-        fail("Failed to get the default launcher for user " + context.getUserId());
+
+        if (size > 1) {
+            fail("Too many launchers for user " + user.getIdentifier() + " using role "
+                    + RoleManager.ROLE_HOME + ": " + roleHolders);
+        } else {
+            fail("No default launcher for user " + user.getIdentifier() + " using role "
+                    + RoleManager.ROLE_HOME);
+        }
         return null;
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index e510b4f..5462f47 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -61,6 +61,7 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.VibrationEffect;
@@ -81,10 +82,12 @@
 import com.android.internal.util.IntPair;
 import com.android.server.UiServiceTestCase;
 import com.android.server.lights.LogicalLight;
+import com.android.server.pm.PackageManagerService;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -412,12 +415,16 @@
     }
 
     private void verifyVibrate() {
+        ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
-                anyString(), any(AudioAttributes.class));
+                anyString(), captor.capture());
+        assertEquals(0, (captor.getValue().getAllFlags()
+                & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
     }
 
     private void verifyVibrate(int times) {
-        verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(),
+        verify(mVibrator, times(times)).vibrate(eq(Process.SYSTEM_UID),
+                eq(PackageManagerService.PLATFORM_PACKAGE_NAME), any(), anyString(),
                 any(AudioAttributes.class));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d8e7582..c19f348 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -159,6 +159,11 @@
         setBooted(mAtm);
     }
 
+    private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
+        return new TestStartingWindowOrganizer(mAtm,
+                mSystemServicesTestRule.getPowerManagerWrapper());
+    }
+
     @Test
     public void testStackCleanupOnClearingTask() {
         final ActivityRecord activity = createActivityWith2LevelTask();
@@ -2294,6 +2299,7 @@
 
     @Test
     public void testCreateRemoveStartingWindow() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -2307,6 +2313,7 @@
 
     @Test
     public void testAddRemoveRace() {
+        registerTestStartingWindowOrganizer();
         // There was once a race condition between adding and removing starting windows
         final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
         for (int i = 0; i < 1000; i++) {
@@ -2321,6 +2328,7 @@
 
     @Test
     public void testTransferStartingWindow() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
@@ -2337,9 +2345,10 @@
 
     @Test
     public void testTransferStartingWindowWhileCreating() {
+        final TestStartingWindowOrganizer organizer = registerTestStartingWindowOrganizer();
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+        organizer.setRunnableWhenAddingSplashScreen(
                 () -> {
                     // Surprise, ...! Transfer window in the middle of the creation flow.
                     activity2.addStartingWindow(mPackageName,
@@ -2357,6 +2366,7 @@
 
     @Test
     public void testTransferStartingWindowCanAnimate() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
@@ -2380,6 +2390,7 @@
 
     @Test
     public void testTransferStartingWindowFromFinishingActivity() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Task task = activity.getTask();
         activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
@@ -2423,6 +2434,7 @@
 
     @Test
     public void testTransferStartingWindowSetFixedRotation() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Task task = activity.getTask();
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -2454,6 +2466,7 @@
 
     @Test
     public void testTryTransferStartingWindowFromHiddenAboveToken() {
+        registerTestStartingWindowOrganizer();
         // Add two tasks on top of each other.
         final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 8703c31..7f9e7da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -154,7 +154,7 @@
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
         when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
-                any(InputChannel.class))).thenReturn(true);
+                any(InputChannel.class), any(boolean.class))).thenReturn(true);
 
         mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
     }
@@ -370,7 +370,7 @@
                     .build();
 
             assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
-                    new InputChannel()));
+                    new InputChannel(), true /* isDragDrop */));
             mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data);
             assertNotNull(mToken);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 9830631..c483ae9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -141,7 +141,7 @@
         assertNull(mProvider.getControlTarget());
 
         // We can have the control and the control target after seamless rotation.
-        mProvider.finishSeamlessRotation(false /* timeout */);
+        mProvider.finishSeamlessRotation();
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index c6be987..7a4ad74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -49,6 +52,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -511,6 +515,8 @@
         final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
         final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
 
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, false);
         verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
 
         final WindowContainer parent = navToken.getParent();
@@ -518,6 +524,8 @@
                 mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
 
         mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, true);
         verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
         verify(navBarFadeAnimationController).fadeWindowToken(true);
     }
@@ -532,6 +540,8 @@
         final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
         final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
 
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, false);
         verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
 
         final WindowContainer parent = navToken.getParent();
@@ -539,6 +549,51 @@
                 mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
 
         mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, true);
+        verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+        verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
+    }
+
+    @Test
+    public void testNotAttachNavigationBar_controlledByFixedRotationAnimation() {
+        setupForShouldAttachNavBarDuringTransition();
+        FixedRotationAnimationController mockController =
+                mock(FixedRotationAnimationController.class);
+        doReturn(mockController).when(mDefaultDisplay).getFixedRotationAnimationController();
+        final ActivityRecord homeActivity = createHomeActivity();
+        initializeRecentsAnimationController(mController, homeActivity);
+        assertFalse(mController.isNavigationBarAttachedToApp());
+    }
+
+    @Test
+    public void testAttachNavBarInSplitScreenMode() {
+        setupForShouldAttachNavBarDuringTransition();
+        final ActivityRecord primary = createActivityRecordWithParentTask(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        final ActivityRecord secondary = createActivityRecordWithParentTask(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        final ActivityRecord homeActivity = createHomeActivity();
+        homeActivity.setVisibility(true);
+        initializeRecentsAnimationController(mController, homeActivity);
+
+        WindowState navWindow = mController.getNavigationBarWindow();
+        final WindowToken navToken = navWindow.mToken;
+        final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
+
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, false);
+        verify(navWindow).setSurfaceTranslationY(-secondary.getBounds().top);
+        verify(transaction).reparent(navToken.getSurfaceControl(), secondary.getSurfaceControl());
+        reset(navWindow);
+
+        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        final WindowContainer parent = navToken.getParent();
+        final NavBarFadeAnimationController navBarFadeAnimationController =
+                mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, true);
+        verify(navWindow).setSurfaceTranslationY(0);
         verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
         verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
     }
@@ -600,9 +655,10 @@
 
     private void setupForShouldAttachNavBarDuringTransition() {
         mController.mShouldAttachNavBarToAppDuringTransition = true;
-        final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+        final WindowState navBar = spy(createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"));
         mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
         mWm.setRecentsAnimationController(mController);
+        doReturn(navBar).when(mController).getNavigationBarWindow();
         final NavBarFadeAnimationController mockNavBarFadeAnimationController =
                 mock(NavBarFadeAnimationController.class);
         final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2fdd63e..c98e013 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -36,6 +36,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 
@@ -208,18 +210,20 @@
     }
 
     @Test
-    public void testZeroAnimations() {
+    public void testZeroAnimations() throws Exception {
         mController.goodToGo(TRANSIT_OLD_NONE);
-        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+        verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
+        verify(mMockRunner).onAnimationCancelled();
     }
 
     @Test
-    public void testNotReallyStarted() {
+    public void testNotReallyStarted() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createRemoteAnimationRecord(win.mActivityRecord,
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+        verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
+        verify(mMockRunner).onAnimationCancelled();
     }
 
     @Test
@@ -250,7 +254,7 @@
     }
 
     @Test
-    public void testRemovedBeforeStarted() {
+    public void testRemovedBeforeStarted() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
@@ -258,7 +262,8 @@
                 mFinishedCallback);
         win.mActivityRecord.removeImmediately();
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+        verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
+        verify(mMockRunner).onAnimationCancelled();
         verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
                 eq(adapter));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e22cda6..eba5634 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -42,16 +42,20 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.app.ActivityOptions;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -87,6 +91,48 @@
     }
 
     @Test
+    public void getOrCreateLaunchRootRespectsResolvedWindowingMode() {
+        final Task rootTask = createTaskStackOnDisplay(
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        rootTask.mCreatedByOrganizer = true;
+        final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
+        taskDisplayArea.setLaunchRootTask(
+                rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+        final Task candidateRootTask = createTaskStackOnDisplay(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+        final LaunchParams launchParams = new LaunchParams();
+        launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
+
+        final Task actualRootTask = taskDisplayArea.getOrCreateRootTask(
+                activity, null /* options */, candidateRootTask,
+                launchParams, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        assertSame(rootTask, actualRootTask.getRootTask());
+    }
+
+    @Test
+    public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() {
+        final Task rootTask = createTaskStackOnDisplay(
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        rootTask.mCreatedByOrganizer = true;
+        final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
+        taskDisplayArea.setLaunchRootTask(
+                rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+        final Task candidateRootTask = createTaskStackOnDisplay(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        final Task actualRootTask = taskDisplayArea.getOrCreateRootTask(
+                activity, options, candidateRootTask,
+                null /* launchParams */, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        assertSame(rootTask, actualRootTask.getRootTask());
+    }
+
+    @Test
     public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
         final Task stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 75226b7..8bc4ced 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -34,7 +34,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.InputChannel;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -63,7 +62,8 @@
 
         when(mWm.mInputManager.transferTouchFocus(
                 any(InputChannel.class),
-                any(InputChannel.class))).thenReturn(true);
+                any(InputChannel.class),
+                any(boolean.class))).thenReturn(true);
 
         mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
         mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
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 ecb8b60..dca6b08 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -26,6 +26,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -277,4 +278,15 @@
         // Orientation request from standard activity in multi window will not be handled.
         assertFalse(leafTask2.handlesOrientationChangeFromDescendant());
     }
+
+    @Test
+    public void testAlwaysOnTop() {
+        final Task task = createTaskStackOnDisplay(mDisplayContent);
+        task.setAlwaysOnTop(true);
+        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        assertTrue(task.isAlwaysOnTop());
+
+        task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
+        assertFalse(task.isAlwaysOnTop());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 86d8eee..ae8e2de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -100,9 +100,9 @@
     }
 
     @Override
-    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
-            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
-            int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+    public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
+            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
+            int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
         final com.android.server.wm.WindowState window;
         final ActivityRecord activity;
         final WindowManagerService wm = mWmSupplier.get();
@@ -119,7 +119,7 @@
             mRunnableWhenAddingSplashScreen.run();
             mRunnableWhenAddingSplashScreen = null;
         }
-        return () -> {
+        return (a) -> {
             synchronized (wm.mGlobalLock) {
                 activity.removeChild(window);
                 activity.mStartingWindow = null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 2c2c09a..01c503e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -547,7 +547,8 @@
             }
 
             @Override
-            public void removeStartingWindow(int taskId) { }
+            public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                    boolean playRevealAnimation) { }
 
             @Override
             public void copySplashScreenView(int taskId) { }
@@ -614,7 +615,8 @@
 
             }
             @Override
-            public void removeStartingWindow(int taskId) { }
+            public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                    boolean playRevealAnimation) { }
             @Override
             public void copySplashScreenView(int taskId) { }
             @Override
@@ -688,7 +690,8 @@
             }
 
             @Override
-            public void removeStartingWindow(int taskId) { }
+            public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                    boolean playRevealAnimation) { }
             @Override
             public void copySplashScreenView(int taskId) { }
             @Override
@@ -832,7 +835,8 @@
         @Override
         public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
         @Override
-        public void removeStartingWindow(int taskId) { }
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) { }
         @Override
         public void copySplashScreenView(int taskId) { }
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 51aec65..5b5b1da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -521,7 +521,7 @@
         matrix.mapPoints(curSurfacePos);
         verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
 
-        app.finishSeamlessRotation(false /* timeout */);
+        app.finishSeamlessRotation(t);
         assertFalse(app.mSeamlesslyRotated);
         assertNull(app.mPendingSeamlessRotate);
 
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 827ff6c..4a7784c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -36,6 +36,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -50,6 +51,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
@@ -67,6 +69,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -74,6 +77,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IDisplayWindowInsetsController;
@@ -100,6 +104,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.HashMap;
 
 /** Common base class for window manager unit test classes. */
 class WindowTestsBase extends SystemServiceTestsBase {
@@ -1123,6 +1128,88 @@
         }
     }
 
+    static class TestStartingWindowOrganizer extends ITaskOrganizer.Stub {
+        private final ActivityTaskManagerService mAtm;
+        private final WindowManagerService mWMService;
+        private final WindowState.PowerManagerWrapper mPowerManagerWrapper;
+
+        private Runnable mRunnableWhenAddingSplashScreen;
+        private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>();
+        private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>();
+
+        TestStartingWindowOrganizer(ActivityTaskManagerService service,
+                WindowState.PowerManagerWrapper powerManagerWrapper) {
+            mAtm = service;
+            mWMService = mAtm.mWindowManager;
+            mPowerManagerWrapper = powerManagerWrapper;
+            if (DEBUG_ENABLE_SHELL_DRAWER) {
+                mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
+                mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
+            }
+        }
+
+        void setRunnableWhenAddingSplashScreen(Runnable r) {
+            if (DEBUG_ENABLE_SHELL_DRAWER) {
+                mRunnableWhenAddingSplashScreen = r;
+            } else {
+                ((TestWindowManagerPolicy) mWMService.mPolicy).setRunnableWhenAddingSplashScreen(r);
+            }
+        }
+
+        @Override
+        public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
+            synchronized (mWMService.mGlobalLock) {
+                final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
+                        appToken);
+                IWindow iWindow = mock(IWindow.class);
+                doReturn(mock(IBinder.class)).when(iWindow).asBinder();
+                final WindowState window = WindowTestsBase.createWindow(null,
+                        TYPE_APPLICATION_STARTING, activity,
+                        "Starting window", 0 /* ownerId */, 0 /* userId*/,
+                        false /* internalWindows */, mWMService, mock(Session.class),
+                        iWindow,
+                        mPowerManagerWrapper);
+                activity.mStartingWindow = window;
+                mAppWindowMap.put(appToken, window);
+                mTaskAppMap.put(info.taskInfo.taskId, appToken);
+            }
+            if (mRunnableWhenAddingSplashScreen != null) {
+                mRunnableWhenAddingSplashScreen.run();
+                mRunnableWhenAddingSplashScreen = null;
+            }
+        }
+        @Override
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
+            synchronized (mWMService.mGlobalLock) {
+                final IBinder appToken = mTaskAppMap.get(taskId);
+                if (appToken != null) {
+                    mTaskAppMap.remove(taskId);
+                    final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
+                            appToken);
+                    WindowState win = mAppWindowMap.remove(appToken);
+                    activity.removeChild(win);
+                    activity.mStartingWindow = null;
+                }
+            }
+        }
+        @Override
+        public void copySplashScreenView(int taskId) {
+        }
+        @Override
+        public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
+        }
+        @Override
+        public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
+        }
+        @Override
+        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+        }
+        @Override
+        public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+        }
+    }
+
     static class TestSplitOrganizer extends ITaskOrganizer.Stub {
         final ActivityTaskManagerService mService;
         Task mPrimary;
@@ -1161,7 +1248,8 @@
         public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
         }
         @Override
-        public void removeStartingWindow(int taskId) {
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
         }
         @Override
         public void copySplashScreenView(int taskId) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index c82ba99..d967891 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -470,7 +470,7 @@
                 mWm, mockRunner, null, displayId);
         spyOn(controller);
         controller.mShouldAttachNavBarToAppDuringTransition = true;
-        doReturn(mNavBarWindow.mToken).when(controller).getNavigationBarWindowToken();
+        doReturn(mNavBarWindow).when(controller).getNavigationBarWindow();
         mWm.setRecentsAnimationController(controller);
 
         // set ime visible
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index ed71d17..e089995a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -17,6 +17,7 @@
 package com.android.server.voiceinteraction;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -25,8 +26,10 @@
 import android.media.AudioAttributes;
 import android.media.AudioRecord;
 import android.media.MediaRecorder;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.service.voice.AlwaysOnHotwordDetector;
 import android.service.voice.HotwordDetectionService;
 import android.service.voice.IDspHotwordDetectionCallback;
@@ -74,7 +77,8 @@
     boolean mBound;
 
     HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName,
-            int userId, boolean bindInstantServiceAllowed) {
+            int userId, boolean bindInstantServiceAllowed, @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
         mLock = lock;
         mContext = context;
         mDetectionComponentName = serviceName;
@@ -90,6 +94,14 @@
                     boolean connected) {
                 synchronized (mLock) {
                     mBound = connected;
+                    if (connected) {
+                        try {
+                            service.setConfig(options, sharedMemory);
+                        } catch (RemoteException e) {
+                            // TODO: (b/181842909) Report an error to voice interactor
+                            Slog.w(TAG, "Failed to setConfig for HotwordDetectionService", e);
+                        }
+                    }
                 }
             }
 
@@ -117,6 +129,11 @@
         }
     }
 
+    void setConfigLocked(Bundle options, SharedMemory sharedMemory) {
+        mRemoteHotwordDetectionService.run(
+                service -> service.setConfig(options, sharedMemory));
+    }
+
     private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
             IHotwordRecognitionStatusCallback externalCallback) {
         if (DEBUG) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index dce63eb..2626bfd 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -60,6 +60,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.SharedMemory;
 import android.os.ShellCallback;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -982,23 +983,46 @@
         }
 
         @Override
-        public int setHotwordDetectionConfig(Bundle options) {
+        public void setHotwordDetectionServiceConfig(@Nullable Bundle options,
+                @Nullable SharedMemory sharedMemory) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
                 if (mImpl == null) {
                     Slog.w(TAG,
-                            "setHotwordDetectionConfig without running voice interaction service");
-                    return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+                            "setHotwordDetectionServiceConfig without running voice"
+                                    + " interaction service");
+                    return;
                 }
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.setHotwordDetectionConfigLocked(options);
+                    mImpl.setHotwordDetectionServiceConfigLocked(options, sharedMemory);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
             }
         }
+
+        @Override
+        public void shutdownHotwordDetectionService() {
+            synchronized (this) {
+                enforceIsCurrentVoiceInteractionService();
+
+                if (mImpl == null) {
+                    Slog.w(TAG,
+                            "shutdownHotwordDetectionService without running voice"
+                                    + " interaction service");
+                    return;
+                }
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.shutdownHotwordDetectionServiceLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+        }
+
         //----------------- Model management APIs --------------------------------//
 
         @Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 04dea3f..5861610 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -43,11 +43,13 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SharedMemory;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionService;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionServiceInfo;
+import android.system.OsConstants;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.IWindowManager;
@@ -389,25 +391,48 @@
         return mInfo.getSupportsLocalInteraction();
     }
 
-    public int setHotwordDetectionConfigLocked(Bundle options) {
+    public void setHotwordDetectionServiceConfigLocked(@Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
         if (DEBUG) {
-            Slog.d(TAG, "setHotwordDetectionConfigLocked");
+            Slog.d(TAG, "setHotwordDetectionServiceConfigLocked");
         }
         if (mHotwordDetectionComponentName == null) {
-            Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
-                    + " name not found");
-            return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+            Slog.w(TAG, "Hotword detection service name not found");
+            throw new IllegalStateException("Hotword detection service name not found");
         }
         if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
-            return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+            Slog.w(TAG, "Hotword detection service not in isolated process");
+            throw new IllegalStateException("Hotword detection service not in isolated process");
         }
         // TODO : Need to check related permissions for hotword detection service
         // TODO : Sanitize for bundle
 
-        mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
-                mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false);
+        if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
+            Slog.w(TAG, "Can't set sharedMemory to be read-only");
+            throw new IllegalStateException("Can't set sharedMemory to be read-only");
+        }
 
-        return VoiceInteractionService.HOTWORD_CONFIG_SUCCESS;
+        if (mHotwordDetectionConnection == null) {
+            mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
+                    mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
+                    options, sharedMemory);
+        } else {
+            mHotwordDetectionConnection.setConfigLocked(options, sharedMemory);
+        }
+    }
+
+    public void shutdownHotwordDetectionServiceLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "shutdownHotwordDetectionServiceLocked");
+        }
+
+        if (mHotwordDetectionConnection == null) {
+            Slog.w(TAG, "shutdown, but no hotword detection connection");
+            return;
+        }
+
+        mHotwordDetectionConnection.cancelLocked();
+        mHotwordDetectionConnection = null;
     }
 
     public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
index 201c5db..5fb6b33 100644
--- a/telecomm/java/android/telecom/CallDiagnosticService.java
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -19,17 +19,23 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.telephony.Annotation;
+import android.telephony.ims.ImsReasonInfo;
 import android.util.ArrayMap;
 
 import com.android.internal.telecom.ICallDiagnosticService;
 import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
 
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the
@@ -51,6 +57,11 @@
  * </service>
  * }
  * </pre>
+ * <p>
+ * <h2>Threading Model</h2>
+ * By default, all incoming IPC from Telecom in this service and in the {@link DiagnosticCall}
+ * instances will take place on the main thread.  You can override {@link #getExecutor()} in your
+ * implementation to provide your own {@link Executor}.
  * @hide
  */
 @SystemApi
@@ -83,7 +94,7 @@
 
         @Override
         public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException {
-            onCallAudioStateChanged(callAudioState);
+            getExecutor().execute(() -> onCallAudioStateChanged(callAudioState));
         }
 
         @Override
@@ -96,6 +107,12 @@
                 throws RemoteException {
             handleBluetoothCallQualityReport(qualityReport);
         }
+
+        @Override
+        public void notifyCallDisconnected(@NonNull String callId,
+                @NonNull DisconnectCause disconnectCause) throws RemoteException {
+            handleCallDisconnected(callId, disconnectCause);
+        }
     }
 
     /**
@@ -133,8 +150,18 @@
      */
     private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>();
     private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+    private final Object mLock = new Object();
     private ICallDiagnosticServiceAdapter mAdapter;
 
+    /**
+     * Handles binding to the {@link CallDiagnosticService}.
+     *
+     * @param intent The Intent that was used to bind to this service,
+     * as given to {@link android.content.Context#bindService
+     * Context.bindService}.  Note that any extras that were included with
+     * the Intent at that point will <em>not</em> be seen here.
+     * @return
+     */
     @Nullable
     @Override
     public IBinder onBind(@NonNull Intent intent) {
@@ -143,11 +170,29 @@
     }
 
     /**
+     * Returns the {@link Executor} to use for incoming IPS from Telecom into your service
+     * implementation.
+     * <p>
+     * Override this method in your {@link CallDiagnosticService} implementation to provide the
+     * executor you want to use for incoming IPC.
+     *
+     * @return the {@link Executor} to use for incoming IPC from Telecom to
+     * {@link CallDiagnosticService} and {@link DiagnosticCall}.
+     */
+    @SuppressLint("OnNameExpected")
+    @NonNull public Executor getExecutor() {
+        return new HandlerExecutor(Handler.createAsync(getMainLooper()));
+    }
+
+    /**
      * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call
      * which was added to Telecom.
      * <p>
      * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be
      * used for the lifespan of this call.
+     * <p>
+     * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+     * {@link CallDiagnosticService#getExecutor()} for more information.
      *
      * @param call The details of the new call.
      * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
@@ -160,6 +205,10 @@
     /**
      * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed.
      * This happens when Telecom is no longer tracking the call in question.
+     * <p>
+     * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+     * {@link CallDiagnosticService#getExecutor()} for more information.
+     *
      * @param call The diagnostic call which is no longer tracked by Telecom.
      */
     public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
@@ -169,6 +218,9 @@
      * changes.
      * <p>
      * Audio state is common to all calls.
+     * <p>
+     * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+     * {@link CallDiagnosticService#getExecutor()} for more information.
      *
      * @param audioState The new audio state.
      */
@@ -178,6 +230,10 @@
     /**
      * Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the
      * bluetooth stack.
+     * <p>
+     * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+     * {@link CallDiagnosticService#getExecutor()} for more information.
+     *
      * @param qualityReport the {@link BluetoothCallQualityReport}.
      */
     public abstract void onBluetoothCallQualityReportReceived(
@@ -199,15 +255,22 @@
         String telecomCallId = parcelableCall.getId();
         Log.i(this, "handleCallAdded: callId=%s - added", telecomCallId);
         Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
-        mCallByTelecomCallId.put(telecomCallId, newCallDetails);
-
-        DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
-        if (diagnosticCall == null) {
-            throw new IllegalArgumentException("A valid DiagnosticCall instance was not provided.");
+        synchronized (mLock) {
+            mCallByTelecomCallId.put(telecomCallId, newCallDetails);
         }
-        diagnosticCall.setListener(mDiagnosticCallListener);
-        diagnosticCall.setCallId(telecomCallId);
-        mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+
+        getExecutor().execute(() -> {
+            DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
+            if (diagnosticCall == null) {
+                throw new IllegalArgumentException(
+                        "A valid DiagnosticCall instance was not provided.");
+            }
+            synchronized (mLock) {
+                diagnosticCall.setListener(mDiagnosticCallListener);
+                diagnosticCall.setCallId(telecomCallId);
+                mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+            }
+        });
     }
 
     /**
@@ -220,10 +283,12 @@
         String telecomCallId = parcelableCall.getId();
         Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId);
         Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
-
-        DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
-        mCallByTelecomCallId.put(telecomCallId, newCallDetails);
-        diagnosticCall.handleCallUpdated(newCallDetails);
+        DiagnosticCall diagnosticCall;
+        synchronized (mLock) {
+            diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+            mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+        }
+        getExecutor().execute(() -> diagnosticCall.handleCallUpdated(newCallDetails));
     }
 
     /**
@@ -236,10 +301,19 @@
         if (mCallByTelecomCallId.containsKey(telecomCallId)) {
             mCallByTelecomCallId.remove(telecomCallId);
         }
-        if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
-            DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
-            // Inform the service of the removed call.
-            onRemoveDiagnosticCall(call);
+
+        DiagnosticCall diagnosticCall;
+        synchronized (mLock) {
+            if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
+                diagnosticCall = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+            } else {
+                diagnosticCall = null;
+            }
+        }
+
+        // Inform the service of the removed call.
+        if (diagnosticCall != null) {
+            getExecutor().execute(() -> onRemoveDiagnosticCall(diagnosticCall));
         }
     }
 
@@ -252,8 +326,40 @@
      */
     private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) {
         Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value);
+        DiagnosticCall diagnosticCall;
+        synchronized (mLock) {
+            diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+        }
+        if (diagnosticCall != null) {
+            getExecutor().execute(
+                    () -> diagnosticCall.onReceiveDeviceToDeviceMessage(message, value));
+        }
+    }
+
+    /**
+     * Handles a request from the Telecom framework to get a disconnect message from the
+     * {@link CallDiagnosticService}.
+     * @param callId The ID of the call.
+     * @param disconnectCause The telecom disconnect cause.
+     */
+    private void handleCallDisconnected(@NonNull String callId,
+            @NonNull DisconnectCause disconnectCause) {
+        Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause);
         DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
-        diagnosticCall.onReceiveDeviceToDeviceMessage(message, value);
+        CharSequence message;
+        if (disconnectCause.getImsReasonInfo() != null) {
+            message = diagnosticCall.onCallDisconnected(disconnectCause.getImsReasonInfo());
+        } else {
+            message = diagnosticCall.onCallDisconnected(
+                    disconnectCause.getTelephonyDisconnectCause(),
+                    disconnectCause.getTelephonyPreciseDisconnectCause());
+        }
+        try {
+            mAdapter.overrideDisconnectMessage(callId, message);
+        } catch (RemoteException e) {
+            Log.w(this, "handleCallDisconnected: call=%s; cause=%s; %s",
+                    callId, disconnectCause, e);
+        }
     }
 
     /**
@@ -265,7 +371,7 @@
     private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport
             qualityReport) {
         Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport);
-        onBluetoothCallQualityReportReceived(qualityReport);
+        getExecutor().execute(() -> onBluetoothCallQualityReportReceived(qualityReport));
     }
 
     /**
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index dc2fb94..f84dd7b 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -338,7 +338,7 @@
      *
      * @param videoState The video state in which to answer the connection.
      */
-    public void onAnswer(int videoState) {}
+    public void onAnswer(@VideoProfile.VideoState int videoState) {}
 
     /**
      * Notifies this Conference, which is in {@code STATE_RINGING}, of
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 97a06a8..d8bd6a5 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2594,9 +2594,9 @@
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
-    public final RemoteConnection createRemoteIncomingConnection(
-            PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+    public final @Nullable RemoteConnection createRemoteIncomingConnection(
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return mRemoteConnectionManager.createRemoteConnection(
                 connectionManagerPhoneAccount, request, true);
     }
@@ -2613,9 +2613,9 @@
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
-    public final RemoteConnection createRemoteOutgoingConnection(
-            PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+    public final @Nullable RemoteConnection createRemoteOutgoingConnection(
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return mRemoteConnectionManager.createRemoteConnection(
                 connectionManagerPhoneAccount, request, false);
     }
@@ -2855,12 +2855,14 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request Details about the incoming conference call.
-     * @return The {@code Conference} object to satisfy this call, or {@code null} to
-     *         not handle the call.
+     * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+     *         failed, the return value will be a result of an invocation of
+     *         {@link Connection#createFailedConnection(DisconnectCause)}.
+     *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
      */
     public @Nullable Conference onCreateIncomingConference(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return null;
     }
 
@@ -2963,8 +2965,8 @@
      * @param request The outgoing connection request.
      */
     public void onCreateOutgoingConferenceFailed(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
     }
 
 
@@ -3028,12 +3030,14 @@
      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
      *         making the connection.
      * @param request Details about the outgoing call.
-     * @return The {@code Conference} object to satisfy this call, or the result of an invocation
-     *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+     *         failed, the return value will be a result of an invocation of
+     *         {@link Connection#createFailedConnection(DisconnectCause)}.
+     *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
      */
     public @Nullable Conference onCreateOutgoingConference(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return null;
     }
 
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java
index a495289..af46b77 100644
--- a/telecomm/java/android/telecom/DiagnosticCall.java
+++ b/telecomm/java/android/telecom/DiagnosticCall.java
@@ -26,15 +26,27 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic
- * information about a mobile call on the device.  The {@link CallDiagnosticService} can generate
- * mid-call diagnostic messages using the {@link #displayDiagnosticMessage(int, CharSequence)} API
- * which provides the user with valuable information about conditions impacting their call and
- * corrective actions.  For example, if the {@link CallDiagnosticService} determines that conditions
- * on the call are degrading, it can inform the user that the call may soon drop and that they
- * can try using a different calling method (e.g. VOIP or WIFI).
+ * information about a mobile call on the device.  A {@link DiagnosticCall} is similar to a
+ * {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic
+ * and messaging capabilities not present on a {@link Call}.  The {@link CallDiagnosticService}
+ * creates a {@link DiagnosticCall} for each {@link Call} on the device.  This means that for each
+ * in progress call on the device, the {@link CallDiagnosticService} will create an instance of
+ * {@link DiagnosticCall}.
+ * <p>
+ * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the
+ * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable
+ * information about conditions impacting their call and corrective actions.  For example, if the
+ * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform
+ * the user that the call may soon drop and that they can try using a different calling method
+ * (e.g. VOIP or WIFI).
+ * <h2>Threading Model</h2>
+ * All incoming IPC from Telecom in this class will use the same {@link Executor} as the
+ * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more
+ * information.
  * @hide
  */
 @SystemApi
@@ -53,15 +65,19 @@
     /**
      * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
      * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
-     * used for the current call.  Based loosely on the
-     * {@link android.telephony.TelephonyManager#getNetworkType(int)} for the call, provides a
-     * high level summary of the call radio access type.
+     * used for the current call.  The call network type communicated here is an intentional
+     * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which
+     * removes some of the resolution inherent in those values; the
+     * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is
+     * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for
+     * efficiency of transport.  For a discussion on the necessity of this simplification, see
+     * {@link #sendDeviceToDeviceMessage(int, int)}.
      * <p>
-     * Valid values:
+     * Valid values are below:
      * <UL>
-     *     <LI>{@link #NETWORK_TYPE_LTE}</LI>
-     *     <LI>{@link #NETWORK_TYPE_IWLAN}</LI>
-     *     <LI>{@link #NETWORK_TYPE_NR}</LI>
+     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI>
+     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI>
+     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI>
      * </UL>
      */
     public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
@@ -69,14 +85,21 @@
     /**
      * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
      * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
-     * used for the current call.  Based loosely on the {@link Connection#EXTRA_AUDIO_CODEC} for a
-     * call.
+     * used for the current call.
+     * <p>
+     * The audio codec communicated here is an intentional simplification of the
+     * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common
+     * variants of these audio codecs.  Other variants of these codecs are reported as the next
+     * closest variant.  For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec
+     * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}.
+     * For a discussion on the necessity of this simplification, see
+     * {@link #sendDeviceToDeviceMessage(int, int)}.
      * <p>
      * Valid values:
      * <UL>
-     *     <LI>{@link #AUDIO_CODEC_EVS}</LI>
-     *     <LI>{@link #AUDIO_CODEC_AMR_WB}</LI>
-     *     <LI>{@link #AUDIO_CODEC_AMR_NB}</LI>
+     *     <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI>
+     *     <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI>
+     *     <LI>{@link Connection#AUDIO_CODEC_AMR}</LI>
      * </UL>
      */
     public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
@@ -122,41 +145,6 @@
     public @interface MessageType {}
 
     /**
-     * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate an LTE network is being used for the
-     * call.
-     */
-    public static final int NETWORK_TYPE_LTE = 1;
-
-    /**
-     * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate WIFI calling is in use for the call.
-     */
-    public static final int NETWORK_TYPE_IWLAN = 2;
-
-    /**
-     * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate a 5G NR (new radio) network is in
-     * used for the call.
-     */
-    public static final int NETWORK_TYPE_NR = 3;
-
-    /**
-     * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the
-     * Enhanced Voice Services (EVS) codec for the call.
-     */
-    public static final int AUDIO_CODEC_EVS = 1;
-
-    /**
-     * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
-     * (adaptive multi-rate) WB (wide band) audio codec.
-     */
-    public static final int AUDIO_CODEC_AMR_WB = 2;
-
-    /**
-     * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
-     * (adaptive multi-rate) NB (narrow band) audio codec.
-     */
-    public static final int AUDIO_CODEC_AMR_NB = 3;
-
-    /**
      * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
      */
     public static final int BATTERY_STATE_LOW = 1;
@@ -183,7 +171,6 @@
 
     private Listener mListener;
     private String mCallId;
-    private Call.Details mCallDetails;
 
     /**
      * @hide
@@ -210,16 +197,10 @@
     }
 
     /**
-     * Returns the latest {@link Call.Details} associated with this {@link DiagnosticCall} as
-     * reported by {@link #onCallDetailsChanged(Call.Details)}.
-     * @return The latest {@link Call.Details}.
-     */
-    public @NonNull Call.Details getCallDetails() {
-        return mCallDetails;
-    }
-
-    /**
      * Telecom calls this method when the details of a call changes.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
      */
     public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
 
@@ -234,6 +215,9 @@
      * devices communicating are using a different version of the protocol, messages the recipient
      * are not aware of are silently discarded.  This means an older client talking to a new client
      * will not receive newer messages and values sent by the new client.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
      */
     public abstract void onReceiveDeviceToDeviceMessage(
             @MessageType int message,
@@ -253,39 +237,19 @@
      * platform due to the extreme bandwidth constraints inherent with underlying device to device
      * communication transports used by the telephony framework.  Device to device communication is
      * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
-     * for a call, or using the DTMF digits A-D as a communication pathway.  Signalling requirements
-     * for DTMF digits place a significant limitation on the amount of information which can be
-     * communicated during a call.
+     * for a call, or using the DTMF digits A-D as a communication pathway.  RTP header extension
+     * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for
+     * a message.  Signalling requirements for DTMF digits place even more significant limitations
+     * on the amount of information which can be communicated during a call, offering only a few
+     * bits of potential information per message.  The messages and values are constrained in order
+     * to meet the limited bandwidth inherent with DTMF signalling.
      * <p>
-     * Allowed message types and values are:
+     * Allowed message types are:
      * <ul>
-     *     <li>{@link #MESSAGE_CALL_NETWORK_TYPE}
-     *         <ul>
-     *             <li>{@link #NETWORK_TYPE_LTE}</li>
-     *             <li>{@link #NETWORK_TYPE_IWLAN}</li>
-     *             <li>{@link #NETWORK_TYPE_NR}</li>
-     *         </ul>
-     *     </li>
-     *     <li>{@link #MESSAGE_CALL_AUDIO_CODEC}
-     *         <ul>
-     *             <li>{@link #AUDIO_CODEC_EVS}</li>
-     *             <li>{@link #AUDIO_CODEC_AMR_WB}</li>
-     *             <li>{@link #AUDIO_CODEC_AMR_NB}</li>
-     *         </ul>
-     *     </li>
-     *     <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}
-     *         <ul>
-     *             <li>{@link #BATTERY_STATE_LOW}</li>
-     *             <li>{@link #BATTERY_STATE_GOOD}</li>
-     *             <li>{@link #BATTERY_STATE_CHARGING}</li>
-     *         </ul>
-     *     </li>
-     *     <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}
-     *         <ul>
-     *             <li>{@link #COVERAGE_POOR}</li>
-     *             <li>{@link #COVERAGE_GOOD}</li>
-     *         </ul>
-     *     </li>
+     *     <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI>
+     *     <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI>
+     *     <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI>
+     *     <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI>
      * </ul>
      * @param message The message type to send.
      * @param value The message value corresponding to the type.
@@ -307,6 +271,9 @@
      * @param preciseDisconnectCause the precise disconnect cause for the call.
      * @return the disconnect message to use in place of the default Telephony message, or
      * {@code null} if the default message will not be overridden.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
      */
     // TODO: Wire in Telephony support for this.
     public abstract @Nullable CharSequence onCallDisconnected(
@@ -323,6 +290,9 @@
      * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
      * @return A user-readable call disconnect message to use in place of the platform-generated
      * disconnect message, or {@code null} if the disconnect message should not be overridden.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
      */
     // TODO: Wire in Telephony support for this.
     public abstract @Nullable CharSequence onCallDisconnected(
@@ -332,6 +302,9 @@
      * Telecom calls this method when a {@link CallQuality} report is received from the telephony
      * stack for a call.
      * @param callQuality The call quality report for this call.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
      */
     public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
 
@@ -375,7 +348,6 @@
      * @hide
      */
     public void handleCallUpdated(@NonNull Call.Details newDetails) {
-        mCallDetails = newDetails;
         onCallDetailsChanged(newDetails);
     }
 }
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1472a4a..ed7b79f 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,13 @@
 
 package android.telecom;
 
+import android.annotation.Nullable;
 import android.media.ToneGenerator;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Annotation;
+import android.telephony.PreciseDisconnectCause;
+import android.telephony.ims.ImsReasonInfo;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -112,6 +116,9 @@
     private CharSequence mDisconnectDescription;
     private String mDisconnectReason;
     private int mToneToPlay;
+    private int mTelephonyDisconnectCause;
+    private int mTelephonyPreciseDisconnectCause;
+    private ImsReasonInfo mImsReasonInfo;
 
     /**
      * Creates a new DisconnectCause.
@@ -155,11 +162,36 @@
      */
     public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
             int toneToPlay) {
+        this(code, label, description, reason, toneToPlay,
+                android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+                PreciseDisconnectCause.ERROR_UNSPECIFIED,
+                null /* imsReasonInfo */);
+    }
+
+    /**
+     * Creates a new DisconnectCause instance.
+     * @param code The code for the disconnect cause.
+     * @param label The localized label to show to the user to explain the disconnect.
+     * @param description The localized description to show to the user to explain the disconnect.
+     * @param reason The reason for the disconnect.
+     * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
+     * @param telephonyDisconnectCause The Telephony disconnect cause.
+     * @param telephonyPreciseDisconnectCause The Telephony precise disconnect cause.
+     * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
+     * @hide
+     */
+    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+            int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
+            @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
+            @Nullable ImsReasonInfo imsReasonInfo) {
         mDisconnectCode = code;
         mDisconnectLabel = label;
         mDisconnectDescription = description;
         mDisconnectReason = reason;
         mToneToPlay = toneToPlay;
+        mTelephonyDisconnectCause = telephonyDisconnectCause;
+        mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause;
+        mImsReasonInfo = imsReasonInfo;
     }
 
     /**
@@ -209,6 +241,33 @@
     }
 
     /**
+     * Returns the telephony {@link android.telephony.DisconnectCause} for the call.
+     * @return The disconnect cause.
+     * @hide
+     */
+    public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() {
+        return mTelephonyDisconnectCause;
+    }
+
+    /**
+     * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call.
+     * @return The precise disconnect cause.
+     * @hide
+     */
+    public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() {
+        return mTelephonyPreciseDisconnectCause;
+    }
+
+    /**
+     * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection.
+     * @return The {@link ImsReasonInfo} or {@code null} if not known.
+     * @hide
+     */
+    public @Nullable ImsReasonInfo getImsReasonInfo() {
+        return mImsReasonInfo;
+    }
+
+    /**
      * Returns the tone to play when disconnected.
      *
      * @return the tone as defined in {@link ToneGenerator} to play when disconnected.
@@ -217,7 +276,8 @@
         return mToneToPlay;
     }
 
-    public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
+    public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR
+            = new Creator<DisconnectCause>() {
         @Override
         public DisconnectCause createFromParcel(Parcel source) {
             int code = source.readInt();
@@ -225,7 +285,11 @@
             CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             String reason = source.readString();
             int tone = source.readInt();
-            return new DisconnectCause(code, label, description, reason, tone);
+            int telephonyDisconnectCause = source.readInt();
+            int telephonyPreciseDisconnectCause = source.readInt();
+            ImsReasonInfo imsReasonInfo = source.readParcelable(null);
+            return new DisconnectCause(code, label, description, reason, tone,
+                    telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo);
         }
 
         @Override
@@ -241,6 +305,9 @@
         TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
         destination.writeString(mDisconnectReason);
         destination.writeInt(mToneToPlay);
+        destination.writeInt(mTelephonyDisconnectCause);
+        destination.writeInt(mTelephonyPreciseDisconnectCause);
+        destination.writeParcelable(mImsReasonInfo, 0);
     }
 
     @Override
@@ -254,7 +321,10 @@
                 + Objects.hashCode(mDisconnectLabel)
                 + Objects.hashCode(mDisconnectDescription)
                 + Objects.hashCode(mDisconnectReason)
-                + Objects.hashCode(mToneToPlay);
+                + Objects.hashCode(mToneToPlay)
+                + Objects.hashCode(mTelephonyDisconnectCause)
+                + Objects.hashCode(mTelephonyPreciseDisconnectCause)
+                + Objects.hashCode(mImsReasonInfo);
     }
 
     @Override
@@ -265,7 +335,11 @@
                     && Objects.equals(mDisconnectLabel, d.getLabel())
                     && Objects.equals(mDisconnectDescription, d.getDescription())
                     && Objects.equals(mDisconnectReason, d.getReason())
-                    && Objects.equals(mToneToPlay, d.getTone());
+                    && Objects.equals(mToneToPlay, d.getTone())
+                    && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause())
+                    && Objects.equals(mTelephonyPreciseDisconnectCause,
+                    d.getTelephonyPreciseDisconnectCause())
+                    && Objects.equals(mImsReasonInfo, d.getImsReasonInfo());
         }
         return false;
     }
@@ -325,6 +399,11 @@
                 + " Label: (" + label + ")"
                 + " Description: (" + description + ")"
                 + " Reason: (" + reason + ")"
-                + " Tone: (" + mToneToPlay + ") ]";
+                + " Tone: (" + mToneToPlay + ") "
+                + " TelephonyCause: " + mTelephonyDisconnectCause + "/"
+                + mTelephonyPreciseDisconnectCause
+                + " ImsReasonInfo: "
+                + mImsReasonInfo
+                + "]";
     }
 }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 17749e8..1677c8c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,8 +314,8 @@
     public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
 
     /**
-     * A URI representing the picture that was downloaded when a call is received or uploaded
-     * when a call is placed.
+     * A {@link Uri} representing the picture that was downloaded when a call is received or
+     * uploaded when a call is placed.
      *
      * This is a content URI within the call log provider which can be used to open a file
      * descriptor. This could be set a short time after a call is added to the Dialer app if the
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
index 65b4d19..fc9879a 100644
--- a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
@@ -18,6 +18,7 @@
 
 import android.telecom.BluetoothCallQualityReport;
 import android.telecom.CallAudioState;
+import android.telecom.DisconnectCause;
 import android.telecom.ParcelableCall;
 import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
 
@@ -34,4 +35,5 @@
     void removeDiagnosticCall(in String callId);
     void receiveDeviceToDeviceMessage(in String callId, int message, int value);
     void receiveBluetoothCallQualityReport(in BluetoothCallQualityReport qualityReport);
+    void notifyCallDisconnected(in String callId, in DisconnectCause disconnectCause);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 763c968..1a71f80 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -27,6 +27,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
+import android.net.ipsec.ike.SaProposal;
 import android.os.Build;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -1340,6 +1341,38 @@
             "support_ims_conference_event_package_on_peer_bool";
 
     /**
+     * Indicates whether the carrier supports the use of RFC8285 compliant RTP header extensions for
+     * the purpose of device to device communication while in a call.
+     * <p>
+     * See also {@link #KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL}.
+     */
+    public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL =
+            "supports_device_to_device_communication_using_rtp_bool";
+
+    /**
+     * Indicates whether the carrier supports the negotiations of RFC8285 compliant RTP header
+     * extensions supported on a call during the Session Description Protocol (SDP).  This option
+     * is only used when {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
+     * {@code true}.
+     * <p>
+     * When {@code true}, the RTP header extensions the platform uses for device to device
+     * communication will be offered to the remote end during the SDP negotiation process.
+     * When {@code false}, the RTP header extensions will not be negotiated during the SDP
+     * negotiation process and the platform will send RTP header extensions without prior
+     * negotiation if {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
+     * {@code true}.
+     */
+    public static final String KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL =
+            "supports_sdp_negotiation_of_d2d_rtp_header_extensions_bool";
+
+    /**
+     * Indicates whether the carrier supports the use of DTMF digits A-D for the purpose of device
+     * to device communication while in a call.
+     */
+    public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL =
+            "supports_device_to_device_communication_using_dtmf_bool";
+
+    /**
      * Determines whether High Definition audio property is displayed in the dialer UI.
      * If {@code false}, remove the HD audio property from the connection so that HD audio related
      * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -4138,9 +4171,11 @@
                 KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int";
 
         /**
-         * Supported DH groups for IKE negotiation. Possible values are {@link #DH_GROUP_NONE},
-         * {@link #DH_GROUP_1024_BIT_MODP}, {@link #DH_GROUP_1536_BIT_MODP}, {@link
-         * #DH_GROUP_2048_BIT_MODP}
+         * Supported DH groups for IKE negotiation. Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP}
          */
         public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
                 KEY_PREFIX + "diffie_hellman_groups_int_array";
@@ -4176,23 +4211,29 @@
 
         /**
          * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child
-         * session. Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link
-         * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * session. Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
         public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
                 KEY_PREFIX + "child_session_aes_cbc_key_size_int_array";
 
         /**
          * List of supported key sizes for AES Counter (CTR) encryption mode of child session.
-         * Possible values are {@link #KEY_LEN_UNUSED},
-         * {@link #KEY_LEN_AES_128}, {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
         public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
                 KEY_PREFIX + "child_session_aes_ctr_key_size_int_array";
 
         /**
          * List of supported encryption algorithms for child session. Possible values are
-         * {@link #ENCRYPTION_ALGORITHM_AES_CBC}
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}
          */
         public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
@@ -4213,8 +4254,11 @@
 
         /**
          * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE
-         * session. Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link
-         * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * session. Possible values:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
         public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
                 KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array";
@@ -4222,24 +4266,31 @@
 
         /**
          * List of supported key sizes for AES Counter (CTR) encryption mode of IKE session.
-         * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
-         * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * Possible values -
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
          public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
                  KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array";
 
         /**
          * List of supported encryption algorithms for IKE session. Possible values are
-         * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_CTR}
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
          */
         public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
 
         /**
-         * List of supported integrity algorithms for IKE session Possible values are {@link
-         * #INTEGRITY_ALGORITHM_NONE}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA1_96}, {@link
-         * #INTEGRITY_ALGORITHM_AES_XCBC_96}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, {@link
-         * #INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
+         * List of supported integrity algorithms for IKE session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_AES_XCBC_96},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_256_128},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_384_192},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
          */
         public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_integrity_algorithms_int_array";
@@ -4259,9 +4310,11 @@
 
         /**
          * List of supported pseudo random function algorithms for IKE session. Possible values are
-         * {@link #PSEUDORANDOM_FUNCTION_HMAC_SHA1}, {@link #PSEUDORANDOM_FUNCTION_AES128_XCBC},
-         * {@link #PSEUDORANDOM_FUNCTION_SHA2_256}, {@link #PSEUDORANDOM_FUNCTION_SHA2_384},
-         * {@link #PSEUDORANDOM_FUNCTION_SHA2_512}
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_HMAC_SHA1},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_AES128_XCBC},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_256},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_384},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_512}
          */
         public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_prf_algorithms_int_array";
@@ -4334,182 +4387,6 @@
         public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
 
         /** @hide */
-        @IntDef({KEY_LEN_UNUSED, KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256})
-        public @interface EncrpytionKeyLengthType {}
-
-        public static final int KEY_LEN_UNUSED = 0;
-        /** AES Encryption/Ciphering Algorithm key length 128 bits. */
-        public static final int KEY_LEN_AES_128 = 128;
-        /** AES Encryption/Ciphering Algorithm key length 192 bits. */
-        public static final int KEY_LEN_AES_192 = 192;
-        /** AES Encryption/Ciphering Algorithm key length 256 bits. */
-        public static final int KEY_LEN_AES_256 = 256;
-
-        /** @hide */
-        @IntDef({
-            DH_GROUP_NONE,
-            DH_GROUP_1024_BIT_MODP,
-            DH_GROUP_1536_BIT_MODP,
-            DH_GROUP_2048_BIT_MODP,
-            DH_GROUP_3072_BIT_MODP,
-            DH_GROUP_4096_BIT_MODP
-        })
-        public @interface DhGroup {}
-
-        /** None Diffie-Hellman Group. */
-        public static final int DH_GROUP_NONE = 0;
-        /**
-         * 1024-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_1024_BIT_MODP = 2;
-        /**
-         * 1536-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_1536_BIT_MODP = 5;
-        /**
-         * 2048-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_2048_BIT_MODP = 14;
-        /**
-         * 3072-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_3072_BIT_MODP = 15;
-        /**
-         * 4096-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_4096_BIT_MODP = 16;
-
-        /** @hide */
-        @IntDef({ENCRYPTION_ALGORITHM_AES_CBC, ENCRYPTION_ALGORITHM_AES_CTR})
-        public @interface EncryptionAlgorithm {}
-
-        /**
-         * AES-CBC Encryption/Ciphering Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
-
-        /**
-         * AES-CTR Encryption/Ciphering Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13;
-
-        /** @hide */
-        @IntDef({
-            INTEGRITY_ALGORITHM_NONE,
-            INTEGRITY_ALGORITHM_HMAC_SHA1_96,
-            INTEGRITY_ALGORITHM_AES_XCBC_96,
-            INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
-            INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
-            INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
-        })
-        public @interface IntegrityAlgorithm {}
-
-        /** None Authentication/Integrity Algorithm. */
-        public static final int INTEGRITY_ALGORITHM_NONE = 0;
-        /**
-         * HMAC-SHA1 Authentication/Integrity Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2;
-        /**
-         * AES-XCBC-96 Authentication/Integrity Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5;
-        /**
-         * HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12;
-        /**
-         * HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13;
-        /**
-         * HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14;
-
-        /** @hide */
-        @IntDef({
-            PSEUDORANDOM_FUNCTION_HMAC_SHA1,
-            PSEUDORANDOM_FUNCTION_AES128_XCBC,
-            PSEUDORANDOM_FUNCTION_SHA2_256,
-            PSEUDORANDOM_FUNCTION_SHA2_384,
-            PSEUDORANDOM_FUNCTION_SHA2_512
-        })
-        public @interface PseudorandomFunction {}
-
-        /**
-         * HMAC-SHA1 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2;
-        /**
-         * AES128-XCBC Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4;
-        /**
-         * HMAC-SHA2-256 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5;
-        /**
-         * HMAC-SHA2-384 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6;
-        /**
-         * HMAC-SHA2-384 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7;
-
-        /** @hide */
         @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
         public @interface IkeIdType {}
 
@@ -4550,31 +4427,33 @@
             defaults.putIntArray(
                     KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY,
                     new int[] {
-                        DH_GROUP_1024_BIT_MODP, DH_GROUP_1536_BIT_MODP, DH_GROUP_2048_BIT_MODP
+                        SaProposal.DH_GROUP_1024_BIT_MODP,
+                        SaProposal.DH_GROUP_1536_BIT_MODP,
+                        SaProposal.DH_GROUP_2048_BIT_MODP
                     });
             defaults.putIntArray(
                     KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
-                    new int[] {ENCRYPTION_ALGORITHM_AES_CBC});
+                    new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
             defaults.putIntArray(
                     KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
-                    new int[] {ENCRYPTION_ALGORITHM_AES_CBC});
+                    new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
             defaults.putIntArray(
                     KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
                     new int[] {
-                        INTEGRITY_ALGORITHM_AES_XCBC_96,
-                        INTEGRITY_ALGORITHM_HMAC_SHA1_96,
-                        INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
-                        INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
-                        INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
+                        SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
                     });
             defaults.putIntArray(
                     KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY,
                     new int[] {
-                        PSEUDORANDOM_FUNCTION_HMAC_SHA1,
-                        PSEUDORANDOM_FUNCTION_AES128_XCBC,
-                        PSEUDORANDOM_FUNCTION_SHA2_256,
-                        PSEUDORANDOM_FUNCTION_SHA2_384,
-                        PSEUDORANDOM_FUNCTION_SHA2_512
+                        SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+                        SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512
                     });
 
             defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_EAP_ONLY);
@@ -4584,16 +4463,28 @@
             defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20);
             defaults.putIntArray(
                     KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
                     new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
@@ -5008,6 +4899,9 @@
         sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
         sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index cf4e677..f7580d7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -416,7 +416,7 @@
      * <p>
      * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
      * subscription cross sim calling enabled
-     * {@link ImsMmTelManager#isCrossSimCallingEnabledByUser()}
+     * {@link ImsMmTelManager#isCrossSimCallingEnabled()}
      * while your app is running. You can also use a {@link android.app.job.JobService}
      * to ensure your app
      * is notified of changes to the {@link Uri} even when it is not running.
@@ -602,6 +602,43 @@
     public @interface SimDisplayNameSource {}
 
     /**
+     * Device status is not shared to a remote party.
+     */
+    public static final int D2D_SHARING_DISABLED = 0;
+
+    /**
+     * Device status is shared with all numbers in the user's contacts.
+     */
+    public static final int D2D_SHARING_ALL_CONTACTS = 1;
+
+    /**
+     * Device status is shared with all starred contacts.
+     */
+    public static final int D2D_SHARING_STARRED_CONTACTS = 2;
+
+    /**
+     * Device status is shared whenever possible.
+     */
+    public static final int D2D_SHARING_ALL = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"D2D_SHARING_"},
+            value = {
+                    D2D_SHARING_DISABLED,
+                    D2D_SHARING_ALL_CONTACTS,
+                    D2D_SHARING_STARRED_CONTACTS,
+                    D2D_SHARING_ALL
+            })
+    public @interface DeviceToDeviceStatusSharing {}
+
+    /**
+     * TelephonyProvider column name for device to device sharing status.
+     * <P>Type: INTEGER (int)</P>
+     */
+    public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING;
+
+    /**
      * TelephonyProvider column name for the color of a SIM.
      * <P>Type: INTEGER (int)</P>
      */
@@ -3374,6 +3411,36 @@
     }
 
     /**
+     * Set the device to device status sharing user preference for a subscription ID. The setting
+     * app uses this method to indicate with whom they wish to share device to device status
+     * information.
+     * @param sharing the status sharing preference
+     * @param subId the unique Subscription ID in database
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharing int sharing,
+            int subId) {
+        if (VDBG) {
+            logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + subId);
+        }
+        setSubscriptionPropertyHelper(subId, "setDeviceToDeviceSharingStatus",
+                (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subId));
+    }
+
+    /**
+     * Returns the user-chosen device to device status sharing preference
+     * @param subId Subscription id of subscription
+     * @return The device to device status sharing preference
+     */
+    public @DeviceToDeviceStatusSharing int getDeviceToDeviceStatusSharing(int subId) {
+        if (VDBG) {
+            logd("[getDeviceToDeviceStatusSharing] + subId: " + subId);
+        }
+        return getIntegerSubscriptionProperty(subId, D2D_STATUS_SHARING, D2D_SHARING_DISABLED,
+                mContext);
+    }
+
+    /**
      * DO NOT USE.
      * This API is designed for features that are not finished at this point. Do not call this API.
      * @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3cb72b5..3084b14 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -139,6 +139,8 @@
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * Provides access to information about the telephony services on
@@ -3147,6 +3149,10 @@
                 return NETWORK_TYPE_BITMASK_LTE_CA;
             case NETWORK_TYPE_NR:
                 return NETWORK_TYPE_BITMASK_NR;
+            case NETWORK_TYPE_IWLAN:
+                return NETWORK_TYPE_BITMASK_IWLAN;
+            case NETWORK_TYPE_IDEN:
+                return (1 << (NETWORK_TYPE_IDEN - 1));
             default:
                 return NETWORK_TYPE_BITMASK_UNKNOWN;
         }
@@ -8642,8 +8648,8 @@
     public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
 
     /**
-     * Set the allowed network types of the device and
-     * provide the reason triggering the allowed network change.
+     * Set the allowed network types of the device and provide the reason triggering the allowed
+     * network change.
      * This can be called for following reasons
      * <ol>
      * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
@@ -8655,10 +8661,15 @@
      * </ol>
      * This API will result in allowing an intersection of allowed network types for all reasons,
      * including the configuration done through other reasons.
+     *
+     * The functionality of this API with the parameter
+     * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API
+     * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of
+     * {@link TelephonyManager#setAllowedNetworkTypes}.
      * <p>
      * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
      * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
      * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param reason the reason the allowed network type change is taking place
@@ -8698,21 +8709,17 @@
      * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
      * specific reason.
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
-     *
      * @param reason the reason the allowed network type change is taking place
      * @return the allowed network type bitmask
      * @throws IllegalStateException    if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(
             enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
             value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
+    @SystemApi
     public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
             @AllowedNetworkTypesReason int reason) {
         if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -8757,6 +8764,25 @@
     }
 
     /**
+     * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}.
+     *
+     * @param networkTypeBitmask The bitmask of allowed network types.
+     * @return the name of the allowed network types
+     * @hide
+     */
+    public static String convertNetworkTypeBitmaskToString(
+            @NetworkTypeBitMask long networkTypeBitmask) {
+        String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+                .filter(x -> {
+                    return (networkTypeBitmask & getBitMaskForNetworkType(x))
+                            == getBitMaskForNetworkType(x);
+                })
+                .mapToObj(x -> getNetworkTypeName(x))
+                .collect(Collectors.joining("|"));
+        return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName;
+    }
+
+    /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -9135,9 +9161,7 @@
     /**
      * Set the user-set status for enriched calling with call composer.
      *
-     * @param status user-set status for enriched calling with call composer;
-     *               it must be either {@link #CALL_COMPOSER_STATUS_ON} or
-     *               {@link #CALL_COMPOSER_STATUS_OFF}.
+     * @param status user-set status for enriched calling with call composer.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -12579,6 +12603,7 @@
                     NETWORK_TYPE_BITMASK_LTE,
                     NETWORK_TYPE_BITMASK_LTE_CA,
                     NETWORK_TYPE_BITMASK_NR,
+                    NETWORK_TYPE_BITMASK_IWLAN
             })
     public @interface NetworkTypeBitMask {}
 
@@ -14609,6 +14634,10 @@
     /**
      * Enable/Disable E-UTRA-NR Dual Connectivity.
      *
+     * This api is supported only if
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+     * returns true.
      * @param nrDualConnectivityState expected NR dual connectivity state
      * This can be passed following states
      * <ol>
@@ -14623,6 +14652,9 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
     public @EnableNrDualConnectivityResult int setNrDualConnectivityState(
             @NrDualConnectivityState int nrDualConnectivityState) {
         try {
@@ -14642,15 +14674,21 @@
 
     /**
      * Is E-UTRA-NR Dual Connectivity enabled.
+     * This api is supported only if
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+     * returns true.
      * @return true if dual connectivity is enabled else false. Enabled state does not mean dual
      * connectivity is active. It means the device is allowed to connect to both primary and
      * secondary cell.
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
     public boolean isNrDualConnectivityEnabled() {
         try {
             ITelephony telephony = getITelephony();
@@ -14902,11 +14940,23 @@
     public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
             "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
 
+    /**
+     * Indicates whether {@link #setNrDualConnectivityState()} and
+     * {@link #isNrDualConnectivityEnabled()} ()} are available.  See comments
+     * on respective methods for more information.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE =
+            "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @StringDef(prefix = "CAPABILITY_", value = {
             CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
             CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
+            CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE
     })
     public @interface RadioInterfaceCapability {}
 
@@ -15218,13 +15268,17 @@
      * </ul>
      * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
      * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
-     * @param nafId Network Application Function(NAF) fully qualified domain name and
-     * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
-     * part is the constant string "3GPP-bootstrapping" (GBA_ME),
-     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
-     * and the latter part shall be the FQDN of the NAF (e.g.
-     * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com",
-     * or "3GPP-bootstrapping-digest@naf1.operator.com").
+     * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain
+     * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts
+     * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME),
+     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest).
+     * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used
+     * for the authentication, which may be set the same as the resource that the application is
+     * going to access. For example, the nafId can be
+     * "https://3GPP-bootstrapping@naf1.operator.com",
+     * "https://3GPP-bootstrapping-uicc@naf1.operator.com",
+     * "https://3GPP-bootstrapping-digest@naf1.operator.com",
+     * "ftps://3GPP-bootstrapping-digest@naf1.operator.com".
      * @param securityProtocol Security protocol identifier between UE and NAF.  See
      * 3GPP TS 33.220 Annex H. Application can use
      * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a764229..ffe5399 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -18,6 +18,7 @@
 package android.telephony.data;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -29,6 +30,7 @@
 import android.telephony.data.ApnSetting.ProtocolType;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -812,11 +814,19 @@
 
         /**
          * Set pdu session id.
+         * <p/>
+         * The id must be between 1 and 15 when linked to a pdu session.  If no pdu session
+         * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}.
          *
          * @param pduSessionId Pdu Session Id of the data call.
          * @return The same instance of the builder.
          */
-        public @NonNull Builder setPduSessionId(int pduSessionId) {
+        public @NonNull Builder setPduSessionId(
+                @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) {
+            Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET,
+                    "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET);
+            Preconditions.checkArgument(pduSessionId <= 15,
+                    "pduSessionId must be less than or equal to 15.");
             mPduSessionId = pduSessionId;
             return this;
         }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f5f29c6..048b329 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -41,6 +41,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Base class of data service. Services that extend DataService must register the service in
@@ -284,11 +285,11 @@
          *
          * Any resources being transferred cannot be released while a
          * handover is underway.
-         *
+         * <p/>
          * If a handover was unsuccessful, then the framework calls
          * {@link DataService#cancelHandover}.  The target transport retains ownership over any of
          * the resources being transferred.
-         *
+         * <p/>
          * If a handover was successful, the framework calls {@link DataService#deactivateDataCall}
          * with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns
          * the transferred resources and is responsible for releasing them.
@@ -299,21 +300,27 @@
          * @hide
          */
         public void startHandover(int cid, @NonNull DataServiceCallback callback) {
+            Objects.requireNonNull(callback, "callback cannot be null");
             // The default implementation is to return unsupported.
-            if (callback != null) {
-                Log.d(TAG, "startHandover: " + cid);
-                callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
-            } else {
-                Log.e(TAG, "startHandover: " + cid + ", callback is null");
-            }
+            Log.d(TAG, "startHandover: " + cid);
+            callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
 
         /**
          * Indicates that a handover was cancelled after a call to
          * {@link DataService#startHandover}. This is called on the source transport.
-         *
+         * <p/>
          * Since the handover was unsuccessful, the source transport retains ownership over any of
          * the resources being transferred and is still responsible for releasing them.
+         * <p/>
+         * The handover can be cancelled up until either:
+         * <ul><li>
+         *     The handover was successful after receiving a successful response from
+         *     {@link DataService#setupDataCall} on the target transport.
+         * </li><li>
+         *     The data call on the source transport was lost.
+         * </li>
+         * </ul>
          *
          * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
          * @param callback The result callback for this request.
@@ -321,13 +328,10 @@
          * @hide
          */
         public void cancelHandover(int cid, @NonNull DataServiceCallback callback) {
+            Objects.requireNonNull(callback, "callback cannot be null");
             // The default implementation is to return unsupported.
-            if (callback != null) {
-                Log.d(TAG, "cancelHandover: " + cid);
-                callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
-            } else {
-                Log.e(TAG, "cancelHandover: " + cid + ", callback is null");
-            }
+            Log.d(TAG, "cancelHandover: " + cid);
+            callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
 
         /**
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index ca1f861..363e47a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -254,15 +254,15 @@
     }
 
     /**
-     * The APN is throttled for the duration specified in
-     * {@link DataCallResponse#getRetryDurationMillis}.  Calling this method unthrottles that
-     * APN.
+     * Unthrottles the APN on the current transport.  There is no matching "APN throttle" method.
+     * Instead, the APN is throttled for the time specified in
+     * {@link DataCallResponse#getRetryDurationMillis}.
      * <p/>
      * see: {@link DataCallResponse#getRetryDurationMillis}
      *
      * @param apn Access Point Name defined by the carrier.
      */
-    public void onApnUnthrottled(@NonNull String apn) {
+    public void onApnUnthrottled(final @NonNull String apn) {
         if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onApnUnthrottled");
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
index 041edc0..406c38b 100644
--- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -184,16 +184,6 @@
         mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
     }
 
-    /**
-     * Creates attributes based off of a parcel
-     * @param in the parcel
-     * @return the attributes
-     */
-    @NonNull
-    public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) {
-        return new EpsBearerQosSessionAttributes(in);
-    }
-
     @Override
     public int describeContents() {
         return 0;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9bb4db8..486f746 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -277,6 +277,10 @@
      * server infrastructure to get the picture. It can be set via
      * {@link #setCallExtra(String, String)}.
      *
+     * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent
+     * directly to the network for consumption by the called party or forwarded directly from the
+     * network to the platform for caching and download.
+     *
      * Reference: RCC.20 Section 2.4.3.2
      */
     public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
@@ -729,6 +733,10 @@
 
     /**
      * Set the call extra value (Parcelable), given the call extra name.
+     *
+     * Note that the {@link Parcelable} provided must be a class defined in the Android API surface,
+     * as opposed to a class defined by your app.
+     *
      * @param name call extra name
      * @param parcelable call extra value
      */
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index fcb4782..4b28296 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -1018,7 +1018,7 @@
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
-    public boolean isCrossSimCallingEnabledByUser() throws ImsException {
+    public boolean isCrossSimCallingEnabled() throws ImsException {
         ITelephony iTelephony = getITelephony();
         if (iTelephony == null) {
             throw new ImsException("Could not find Telephony Service.",
@@ -1058,7 +1058,7 @@
      * the IMS service is not available.
      * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
      *                 false otherwise
-     * @see #isCrossSimCallingEnabledByUser()
+     * @see #isCrossSimCallingEnabled()
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 85cd81b..abc5606 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -1010,6 +1010,16 @@
                 }
             }
 
+            @Override
+            public void onPreProvisioningReceived(byte[] configXml) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
             private void setExecutor(Executor executor) {
                 mExecutor = executor;
             }
@@ -1022,7 +1032,7 @@
          * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
          * server) or other operator defined triggers. If RCS provisioning is already
          * completed at the time of callback registration, then this method shall be
-         * invoked with the current configuration
+         * invoked with the current configuration.
          * @param configXml The RCS configuration XML received by OTA. It is defined
          * by GSMA RCC.07.
          */
@@ -1055,6 +1065,20 @@
          */
         public void onRemoved() {}
 
+        /**
+         * Some carriers using ACS (auto configuration server) may send a carrier-specific
+         * pre-provisioning configuration XML if the user has not been provisioned for RCS
+         * services yet. When this provisioning XML is received, the framework will move
+         * into a "not provisioned" state for RCS. In order for provisioning to proceed,
+         * the application must parse this configuration XML and perform the carrier specific
+         * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration}
+         * must be called in order for the device to move out of this state and try to fetch
+         * the RCS provisioning information.
+         *
+         * @param configXml the pre-provisioning config in carrier specified format.
+         */
+        public void onPreProvisioningReceived(@NonNull byte[] configXml) {}
+
         /**@hide*/
         public final IRcsConfigCallback getBinder() {
             return mBinder;
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index cedf48b..9c28c36 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -25,7 +25,6 @@
 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;
@@ -389,22 +388,6 @@
          * 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;
@@ -534,14 +517,6 @@
         return mContactUri;
     }
 
-    /**
-     * @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/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 815c08d..dd91026 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -439,8 +439,7 @@
 
         /**
          * The pending request has resulted in an error and may need to be retried, depending on the
-         * error code. The callback {@link #onCapabilitiesReceived(List)}
-         * for each contacts is required to be called before {@link #onError} is called.
+         * error code.
          * @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.
@@ -487,93 +486,6 @@
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
-    @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
-            Manifest.permission.READ_CONTACTS})
-    public void requestCapabilities(@NonNull List<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(), 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);
-        }
-    }
-
-    /**
-     * 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})
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
index 5a8973e..d0853d1 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
@@ -25,5 +25,6 @@
     void onAutoConfigurationErrorReceived(int errorCode, String errorString);
     void onConfigurationReset();
     void onRemoved();
+    void onPreProvisioningReceived(in byte[] config);
 }
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 21aeb64..d75da90 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -552,11 +552,34 @@
         }
         mRcsCallbacks.broadcastAction(c -> {
             try {
-                //TODO compressed by default?
                 c.onAutoConfigurationErrorReceived(errorCode, errorString);
             } catch (RemoteException e) {
                 Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
             }
         });
     }
+
+    /**
+     * Notifies application that pre-provisioning config is received.
+     *
+     * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific
+     * pre-provisioning configuration XML if the user has not been provisioned for RCS
+     * services yet. When such provisioning XML is received, ACS client must call this
+     * method to notify the application with the XML.
+     *
+     * @param configXml the pre-provisioning config in carrier specified format.
+     */
+    public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) {
+        // can be null in testing
+        if (mRcsCallbacks == null) {
+            return;
+        }
+        mRcsCallbacks.broadcastAction(c -> {
+            try {
+                c.onPreProvisioningReceived(configXml);
+            } catch (RemoteException e) {
+                Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+            }
+        });
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 00c9168..03e17fb 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -386,41 +386,6 @@
      * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
      * framework to finish listening for NOTIFY responses.
      *
-     * @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")
-    public void subscribeForCapabilities(@NonNull List<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 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.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 571efce..9493c76 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -300,4 +300,6 @@
     boolean canDisablePhysicalSubscription();
 
     int setUiccApplicationsEnabled(boolean enabled, int subscriptionId);
+
+    int setDeviceToDeviceStatusSharing(int sharing, int subId);
 }
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index b2719fb..896ec9a 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -11,6 +11,8 @@
         <option name="force-skip-system-props" value="true" />
         <!-- set WM tracing verbose level to all -->
         <option name="run-command" value="cmd window tracing level all" />
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame" />
         <!-- restart launcher to activate TAPL -->
         <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
     </target_preparer>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index fbf18d4..c92d40c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,37 +16,12 @@
 
 package com.android.server.wm.flicker.close
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    this.setRotation(testSpec.config.startRotation)
-                    testApp.launchViaIntent(wmHelper)
-                }
-            }
+class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             transitions {
                 device.pressBack()
                 wmHelper.waitForHomeActivityVisible()
             }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun launcherReplacesAppWindowAsTopWindow() =
-        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
-        Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest(bugId = 173684672)
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @FlakyTest(bugId = 173684672)
-    @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 08d2b7c..1f880f6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,37 +16,12 @@
 
 package com.android.server.wm.flicker.close
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    testApp.launchViaIntent(wmHelper)
-                    this.setRotation(testSpec.config.startRotation)
-                }
-            }
+class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             transitions {
                 device.pressHome()
                 wmHelper.waitForHomeActivityVisible()
             }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun launcherReplacesAppWindowAsTopWindow() =
-        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(
-        testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest(bugId = 173689015)
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @FlakyTest(bugId = 173689015)
-    @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
new file mode 100644
index 0000000..fef49d9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.wm.flicker.close
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
+import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
+import org.junit.Assume
+import org.junit.Test
+
+abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
+    protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+    protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+        setup {
+            eachRun {
+                testApp.launchViaIntent(wmHelper)
+                this.setRotation(testSpec.config.startRotation)
+            }
+        }
+        teardown {
+            test {
+                testApp.exit()
+            }
+        }
+    }
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            transition(testSpec.config)
+        }
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarWindowIsAlwaysVisible() {
+        testSpec.navBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarWindowIsAlwaysVisible() {
+        testSpec.statusBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerIsAlwaysVisible() {
+        testSpec.navBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsAlwaysVisible() {
+        testSpec.statusBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @FlakyTest
+    @Test
+    open fun navBarLayerRotatesAndScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @FlakyTest
+    @Test
+    open fun statusBarLayerRotatesScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @FlakyTest(bugId = 173689015)
+    @Test
+    open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest(bugId = 173689015)
+    @Test
+    open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @Presubmit
+    @Test
+    open fun noUncoveredRegions() {
+        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @Presubmit
+    @Test
+    open fun launcherReplacesAppWindowAsTopWindow() {
+        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+    }
+
+    @Presubmit
+    @Test
+    open fun wallpaperWindowBecomesVisible() {
+        testSpec.wallpaperWindowBecomesVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun wallpaperLayerReplacesAppLayer() {
+        testSpec.wallpaperLayerReplacesAppLayer(testApp)
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 74f002d..56ed21b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,37 +16,14 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -59,114 +36,25 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
+class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
                 eachRun {
                     this.setRotation(testSpec.config.startRotation)
                 }
             }
-            transitions {
-                testApp.launchViaIntent(wmHelper)
-                // wmHelper.waitForFullScreenApp(testApp.component)
-            }
             teardown {
                 eachRun {
-                    testApp.exit()
-                    wmHelper.waitForAppTransitionIdle()
-                    this.setRotation(Surface.ROTATION_0)
+                    testApp.exit(wmHelper)
                 }
             }
+            transitions {
+                testApp.launchViaIntent(wmHelper)
+                wmHelper.waitForFullScreenApp(testApp.component)
+            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @Presubmit
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow() =
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
-    @Presubmit
-    @Test
-    // During testing the launcher is always in portrait mode
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
-        Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun appLayerReplacesWallpaperLayer() =
-        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
-    @FlakyTest
-    @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 18fac6a..4a32a9e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,36 +16,19 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
-import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -61,18 +44,12 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
+class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             setup {
                 test {
-                    device.wakeUpAndGoToHomeScreen()
                     testApp.launchViaIntent(wmHelper)
                 }
                 eachRun {
@@ -87,71 +64,50 @@
                 device.reopenAppFromOverview(wmHelper)
                 wmHelper.waitForFullScreenApp(testApp.component)
             }
-            teardown {
-                test {
-                    testApp.exit()
-                }
-            }
         }
+
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+        super.appWindowReplacesLauncherAsTopWindow()
+
+    @Test
+    override fun wallpaperWindowBecomesInvisible() {
+        testSpec.wallpaperWindowBecomesInvisible()
     }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow() =
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
-    @Test
-    fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
-    @Presubmit
-    @Test
-    fun appLayerReplacesWallpaperLayer() =
-        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() {
+    override fun statusBarLayerIsAlwaysVisible() {
         Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerIsAlwaysVisible()
+        super.statusBarLayerIsAlwaysVisible()
     }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() {
+    override fun navBarLayerIsAlwaysVisible() {
         Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerIsAlwaysVisible()
+        super.navBarLayerIsAlwaysVisible()
+    }
+
+    @FlakyTest
+    @Test
+    fun statusBarLayerIsAlwaysVisible_Flaky() {
+        Assume.assumeFalse(testSpec.isRotated)
+        super.statusBarLayerIsAlwaysVisible()
+    }
+
+    @FlakyTest
+    @Test
+    fun navBarLayerIsAlwaysVisible_Flaky() {
+        Assume.assumeFalse(testSpec.isRotated)
+        super.navBarLayerIsAlwaysVisible()
     }
 
     @Presubmit
     @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         Assume.assumeFalse(testSpec.isRotated)
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
     }
 
     @FlakyTest
@@ -163,9 +119,9 @@
 
     @Presubmit
     @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         Assume.assumeFalse(testSpec.isRotated)
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
     @FlakyTest
@@ -175,11 +131,6 @@
         testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
new file mode 100644
index 0000000..e9f0534
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
+import org.junit.Assume
+import org.junit.Test
+
+abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
+    protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+    protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+        withTestName { testSpec.name }
+        repeat { testSpec.config.repetitions }
+        setup {
+            test {
+                device.wakeUpAndGoToHomeScreen()
+                this.setRotation(testSpec.config.startRotation)
+            }
+        }
+        teardown {
+            test {
+                testApp.exit()
+            }
+        }
+    }
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            transition(testSpec.config)
+        }
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarWindowIsAlwaysVisible() {
+        testSpec.navBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerIsAlwaysVisible() {
+        testSpec.navBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @FlakyTest
+    @Test
+    open fun navBarLayerRotatesAndScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarWindowIsAlwaysVisible() {
+        testSpec.statusBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsAlwaysVisible() {
+        testSpec.statusBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @FlakyTest
+    @Test
+    open fun statusBarLayerRotatesScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest
+    @Test
+    open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @Presubmit
+    @Test
+    // During testing the launcher is always in portrait mode
+    open fun noUncoveredRegions() {
+        testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun focusChanges() {
+        testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+    }
+
+    @Presubmit
+    @Test
+    open fun appLayerReplacesWallpaperLayer() {
+        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+    }
+
+    @Presubmit
+    @Test
+    open fun appWindowReplacesLauncherAsTopWindow() {
+        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+    }
+
+    @Presubmit
+    @Test
+    open fun wallpaperWindowBecomesInvisible() {
+        testSpec.wallpaperWindowBecomesInvisible()
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index b61310a..a8b5ea1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,34 +16,14 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,20 +38,13 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
+class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             setup {
                 test {
-                    device.wakeUpAndGoToHomeScreen()
                     testApp.launchViaIntent(wmHelper)
-                    // wmHelper.waitForFullScreenApp(testApp.component)
                 }
                 eachRun {
                     device.pressHome()
@@ -79,93 +52,21 @@
                     this.setRotation(testSpec.config.startRotation)
                 }
             }
+            teardown {
+                eachRun {
+                    testApp.exit(wmHelper)
+                }
+            }
             transitions {
                 testApp.launchViaIntent(wmHelper)
                 wmHelper.waitForFullScreenApp(testApp.component)
             }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
     @FlakyTest
     @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @Presubmit
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow() =
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
-    @Presubmit
-    @Test
-    // During testing the launcher is always in portrait mode
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
-        Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun appLayerReplacesWallpaperLayer() =
-        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 335c8d0..eacf5b2 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,14 +9,17 @@
 
 android_test {
     name: "InputTests",
-    srcs: ["src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     platform_apis: true,
     certificate: "platform",
     static_libs: [
-            "androidx.test.ext.junit",
-            "androidx.test.rules",
-            "truth-prebuilt",
-            "ub-uiautomator",
-        ],
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "truth-prebuilt",
+        "ub-uiautomator",
+    ],
     test_suites: ["device-tests"],
 }
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
new file mode 100644
index 0000000..6350077
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputDeviceTest {
+    private static final float DELTA = 0.01f;
+    private static final int DEVICE_ID = 1000;
+
+    private void assertMotionRangeEquals(InputDevice.MotionRange range,
+            InputDevice.MotionRange outRange) {
+        assertEquals(range.getAxis(), outRange.getAxis());
+        assertEquals(range.getSource(), outRange.getSource());
+        assertEquals(range.getMin(), outRange.getMin(), DELTA);
+        assertEquals(range.getMax(), outRange.getMax(), DELTA);
+        assertEquals(range.getFlat(), outRange.getFlat(), DELTA);
+        assertEquals(range.getFuzz(), outRange.getFuzz(), DELTA);
+        assertEquals(range.getResolution(), outRange.getResolution(), DELTA);
+    }
+
+    private void assertDeviceEquals(InputDevice device, InputDevice outDevice) {
+        assertEquals(device.getId(), outDevice.getId());
+        assertEquals(device.getGeneration(), outDevice.getGeneration());
+        assertEquals(device.getControllerNumber(), outDevice.getControllerNumber());
+        assertEquals(device.getName(), outDevice.getName());
+        assertEquals(device.getVendorId(), outDevice.getVendorId());
+        assertEquals(device.getProductId(), outDevice.getProductId());
+        assertEquals(device.getDescriptor(), outDevice.getDescriptor());
+        assertEquals(device.isExternal(), outDevice.isExternal());
+        assertEquals(device.getSources(), outDevice.getSources());
+        assertEquals(device.getKeyboardType(), outDevice.getKeyboardType());
+        assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
+
+        KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
+        KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap();
+        assertTrue("keyCharacterMap not equal", keyCharacterMap.equals(outKeyCharacterMap));
+
+        for (int j = 0; j < device.getMotionRanges().size(); j++) {
+            assertMotionRangeEquals(device.getMotionRanges().get(j),
+                    outDevice.getMotionRanges().get(j));
+        }
+    }
+
+    private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) {
+        final InputDevice device =
+                new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name",
+                0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+                0 /* sources */, 0 /* keyboardType */, keyCharacterMap,
+                false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+                true /* hasSensor */, false /* hasBattery */);
+
+        Parcel parcel = Parcel.obtain();
+        device.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        InputDevice outDevice = InputDevice.CREATOR.createFromParcel(parcel);
+        assertDeviceEquals(device, outDevice);
+    }
+
+    @Test
+    public void testParcelUnparcelInputDevice_VirtualCharacterMap() {
+        final KeyCharacterMap keyCharacterMap =
+                KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+        assertInputDeviceParcelUnparcel(keyCharacterMap);
+    }
+
+    @Test
+    public void testParcelUnparcelInputDevice_EmptyCharacterMap() {
+        final KeyCharacterMap keyCharacterMap = KeyCharacterMap.obtainEmptyMap(DEVICE_ID);
+        assertInputDeviceParcelUnparcel(keyCharacterMap);
+    }
+}
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index 2e985fb..b9b347b 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -43,7 +43,7 @@
             xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
 }
 
-fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
+private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
     val code = KeyEvent.KEYCODE_A
     val repeat = 0
     return KeyEvent(eventTime, eventTime, action, code, repeat)
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
new file mode 100644
index 0000000..4f95ce5
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.os.HandlerThread
+import android.os.Looper
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputEventSender
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.CountDownLatch
+import org.junit.Assert.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
+    assertEquals(expected.action, received.action)
+    assertEquals(expected.deviceId, received.deviceId)
+    assertEquals(expected.downTime, received.downTime)
+    assertEquals(expected.eventTime, received.eventTime)
+    assertEquals(expected.keyCode, received.keyCode)
+    assertEquals(expected.scanCode, received.scanCode)
+    assertEquals(expected.repeatCount, received.repeatCount)
+    assertEquals(expected.metaState, received.metaState)
+    assertEquals(expected.flags, received.flags)
+    assertEquals(expected.source, received.source)
+    assertEquals(expected.displayId, received.displayId)
+}
+
+class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+        InputEventReceiver(channel, looper) {
+    companion object {
+        const val TAG = "TestInputEventReceiver"
+    }
+
+    var lastEvent: InputEvent? = null
+
+    override fun onInputEvent(event: InputEvent) {
+        lastEvent = when (event) {
+            is KeyEvent -> KeyEvent.obtain(event)
+            is MotionEvent -> MotionEvent.obtain(event)
+            else -> throw Exception("Received $event is neither a key nor a motion")
+        }
+        finishInputEvent(event, true /*handled*/)
+    }
+}
+
+class TestInputEventSender(channel: InputChannel, looper: Looper) :
+        InputEventSender(channel, looper) {
+    companion object {
+        const val TAG = "TestInputEventSender"
+    }
+    data class FinishedResult(val seq: Int, val handled: Boolean)
+
+    private var mFinishedSignal = CountDownLatch(1)
+    override fun onInputEventFinished(seq: Int, handled: Boolean) {
+        finishedResult = FinishedResult(seq, handled)
+        mFinishedSignal.countDown()
+    }
+    lateinit var finishedResult: FinishedResult
+
+    fun waitForFinish() {
+        mFinishedSignal.await()
+        mFinishedSignal = CountDownLatch(1) // Ready for next event
+    }
+}
+
+class InputEventSenderAndReceiverTest {
+    companion object {
+        private const val TAG = "InputEventSenderAndReceiverTest"
+    }
+    private val mHandlerThread = HandlerThread("Process input events")
+    private lateinit var mReceiver: TestInputEventReceiver
+    private lateinit var mSender: TestInputEventSender
+
+    @Before
+    fun setUp() {
+        val channels = InputChannel.openInputChannelPair("TestChannel")
+        mHandlerThread.start()
+
+        val looper = mHandlerThread.getLooper()
+        mSender = TestInputEventSender(channels[0], looper)
+        mReceiver = TestInputEventReceiver(channels[1], looper)
+    }
+
+    @After
+    fun tearDown() {
+        mHandlerThread.quitSafely()
+    }
+
+    @Test
+    fun testSendAndReceiveKey() {
+        val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_A, 0 /*repeat*/)
+        val seq = 10
+        mSender.sendInputEvent(seq, key)
+        mSender.waitForFinish()
+
+        // Check receiver
+        assertKeyEvent(key, mReceiver.lastEvent!! as KeyEvent)
+
+        // Check sender
+        assertEquals(seq, mSender.finishedResult.seq)
+        assertEquals(true, mSender.finishedResult.handled)
+    }
+}
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index d6846fa..df58da5 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -94,10 +94,7 @@
             if (platformCompat == null) {
                 throw new IllegalStateException("Could not get IPlatformCompat service!");
             }
-            uiAutomation.adoptShellPermissionIdentity(
-                    Manifest.permission.LOG_COMPAT_CHANGE,
-                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
-                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+            adoptShellPermissions(uiAutomation);
             Compatibility.setOverrides(mConfig);
             try {
                 platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
@@ -105,6 +102,7 @@
                 try {
                     mTestStatement.evaluate();
                 } finally {
+                    adoptShellPermissions(uiAutomation);
                     platformCompat.clearOverridesForTest(packageName);
                 }
             } catch (RemoteException e) {
@@ -114,5 +112,12 @@
                 Compatibility.clearOverrides();
             }
         }
+
+        private static void adoptShellPermissions(UiAutomation uiAutomation) {
+            uiAutomation.adoptShellPermissionIdentity(
+                    Manifest.permission.LOG_COMPAT_CHANGE,
+                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
+                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+        }
     }
 }
diff --git a/tests/RollbackTest/MultiUserRollbackTest.xml b/tests/RollbackTest/MultiUserRollbackTest.xml
index 2f62af1..8fa0510 100644
--- a/tests/RollbackTest/MultiUserRollbackTest.xml
+++ b/tests/RollbackTest/MultiUserRollbackTest.xml
@@ -20,6 +20,8 @@
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.rollback.host.MultiUserRollbackTest" />
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest.xml b/tests/RollbackTest/NetworkStagedRollbackTest.xml
index 2ab907a..13f6031 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest.xml
+++ b/tests/RollbackTest/NetworkStagedRollbackTest.xml
@@ -24,6 +24,8 @@
         <option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\\*' --esa flags &quot;ModuleConfig__versioned_immediate_commit_packages&quot; --esa types &quot;bytes&quot; --esa values &quot;Cm5vdGFwYWNrYWdlOgA=&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__immediate_commit_packages&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__versioned_immediate_commit_packages&quot; com.google.android.gms" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.rollback.host.NetworkStagedRollbackTest" />
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 7b85cc8..fbb6e46 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -27,6 +27,8 @@
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/StagedRollbackTest.xml b/tests/RollbackTest/StagedRollbackTest.xml
index 83fef8e..0ca4daf 100644
--- a/tests/RollbackTest/StagedRollbackTest.xml
+++ b/tests/RollbackTest/StagedRollbackTest.xml
@@ -24,6 +24,8 @@
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.rollback.host.StagedRollbackTest" />
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
index ce226fd..926ff4d 100644
--- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -130,6 +130,9 @@
         return result;
     }
     sBuffers[slot] = anb;
+    if (timeoutMs == 0) {
+        return android::OK;
+    }
     android::sp<android::Fence> fence(new android::Fence(fenceFd));
     int waitResult = fence->wait(timeoutMs);
     if (waitResult != android::OK) {
@@ -197,6 +200,28 @@
     return result;
 }
 
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetAsyncMode(JNIEnv* /* env */,
+                                                                              jclass /* clazz */,
+                                                                              jboolean async) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    return surface->setAsyncMode(async);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetDequeueTimeout(
+        JNIEnv* /* env */, jclass /* clazz */, jlong timeoutMs) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    return surface->setDequeueTimeout(timeoutMs);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetMaxDequeuedBufferCount(
+        JNIEnv* /* env */, jclass /* clazz */, jint maxDequeuedBuffers) {
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    return surface->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+}
+
 JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount(
         JNIEnv* /* env */, jclass /* clazz */, jint count) {
     assert(sAnw);
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
index 7d278dc..b67dc380 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -17,6 +17,7 @@
 
 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
 import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -93,4 +94,80 @@
 
         assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
     }
+
+    @Test
+    // Leave IGBP in sync mode, try to dequeue and queue as fast as possible. Check that we
+    // occasionally get timeout errors.
+    fun testSyncMode_dequeueWithoutBlockingFails() {
+        val numFrames = 1000L
+        runOnUiThread { activity ->
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+            var failures = false
+            for (i in 1..numFrames) {
+                if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+                    failures = true
+                    break
+                }
+                activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+            }
+            assertTrue(failures)
+        }
+    }
+
+    @Test
+    // Set IGBP to be in async mode, try to dequeue and queue as fast as possible. Client should be
+    // able to dequeue and queue buffers without being blocked.
+    fun testAsyncMode_dequeueWithoutBlocking() {
+        val numFrames = 1000L
+        runOnUiThread { activity ->
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true))
+            for (i in 1..numFrames) {
+                assertEquals(0, activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */))
+                activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+            }
+        }
+    }
+
+    @Test
+    // Disable triple buffering in the system and leave IGBP in sync mode. Check that we
+    // occasionally get timeout errors.
+    fun testSyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() {
+        val numFrames = 1000L
+        runOnUiThread { activity ->
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1))
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+            var failures = false
+            for (i in 1..numFrames) {
+                if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+                    failures = true
+                    break
+                }
+                activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+            }
+            assertTrue(failures)
+        }
+    }
+
+    @Test
+    // Disable triple buffering in the system and set IGBP to be in async mode. Try to dequeue and
+    // queue as fast as possible. Without triple buffering, the client does not have an extra buffer
+    // to dequeue and will not be able to dequeue and queue buffers without being blocked.
+    fun testAsyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() {
+        val numFrames = 1000L
+        runOnUiThread { activity ->
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1))
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L))
+            assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true))
+            var failures = false
+            for (i in 1..numFrames) {
+                if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) {
+                    failures = true
+                    break
+                }
+                activity.mSurfaceProxy.SurfaceQueueBuffer(0)
+            }
+            assertTrue(failures)
+        }
+    }
 }
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
index cfbd3ac..45a7094 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -54,7 +54,13 @@
     external fun SurfaceSetScalingMode(scalingMode: Int)
     external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int
     external fun SurfaceCancelBuffer(slot: Int)
-    external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true)
+    external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true): Int
+    external fun SurfaceSetAsyncMode(async: Boolean): Int
+    external fun SurfaceSetDequeueTimeout(timeout: Long): Int
+    external fun SurfaceQuery(what: Int): Int
+    external fun SurfaceSetMaxDequeuedBufferCount(maxDequeuedBuffers: Int): Int
+
+    // system/native_window.h functions
     external fun NativeWindowSetBufferCount(count: Int): Int
     external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int
     external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 7f0318a..e1a424f 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -79,6 +79,7 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "ServiceConnectivityResources",
     ],
     jni_libs: [
         "libservice-connectivity",
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java
index 9c0fc7c..50ecb42 100644
--- a/tests/net/common/java/android/net/IpPrefixTest.java
+++ b/tests/net/common/java/android/net/IpPrefixTest.java
@@ -113,6 +113,15 @@
             p = new IpPrefix("f00:::/32");
             fail("Expected IllegalArgumentException: invalid IPv6 address");
         } catch (IllegalArgumentException expected) { }
+
+        p = new IpPrefix("/64");
+        assertEquals("::/64", p.toString());
+
+        p = new IpPrefix("/128");
+        assertEquals("::1/128", p.toString());
+
+        p = new IpPrefix("[2001:db8::123]/64");
+        assertEquals("2001:db8::/64", p.toString());
     }
 
     @Test
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 1eaf30c..2cf3cf9 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -53,6 +53,7 @@
 import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
@@ -117,6 +118,20 @@
         assertEquals(456, address.getScope());
         assertTrue(address.isIpv4());
 
+        address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */);
+        assertEquals(Inet6Address.LOOPBACK, address.getAddress());
+        assertEquals(64, address.getPrefixLength());
+        assertEquals(1, address.getFlags());
+        assertEquals(2, address.getScope());
+        assertTrue(address.isIpv6());
+
+        address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */);
+        assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress());
+        assertEquals(64, address.getPrefixLength());
+        assertEquals(3, address.getFlags());
+        assertEquals(4, address.getScope());
+        assertTrue(address.isIpv6());
+
         // InterfaceAddress doesn't have a constructor. Fetch some from an interface.
         List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
 
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index e84b992..0dfec75 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -28,6 +28,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
@@ -44,6 +45,10 @@
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 import static android.os.Process.INVALID_UID;
 
+import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.testutils.MiscAsserts.assertEmpty;
+import static com.android.testutils.MiscAsserts.assertThrows;
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
 import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -67,7 +72,6 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.modules.utils.build.SdkLevel;
 import com.android.testutils.CompatUtil;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -85,6 +89,9 @@
 public class NetworkCapabilitiesTest {
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+    private static final int TEST_SUBID1 = 1;
+    private static final int TEST_SUBID2 = 2;
+    private static final int TEST_SUBID3 = 3;
 
     @Rule
     public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
@@ -92,14 +99,6 @@
     private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
     private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
 
-    private boolean isAtLeastR() {
-        return SdkLevel.isAtLeastR();
-    }
-
-    private boolean isAtLeastS() {
-        return SdkLevel.isAtLeastS();
-    }
-
     @Test
     public void testMaybeMarkCapabilitiesRestricted() {
         // verify EIMS is restricted
@@ -305,7 +304,9 @@
             .setUids(uids)
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
-        if (isAtLeastR()) {
+        if (isAtLeastS()) {
+            netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+        } else if (isAtLeastR()) {
             netCap.setOwnerUid(123);
             netCap.setAdministratorUids(new int[] {5, 11});
         }
@@ -380,7 +381,7 @@
 
     private void testParcelSane(NetworkCapabilities cap) {
         if (isAtLeastS()) {
-            assertParcelSane(cap, 16);
+            assertParcelSane(cap, 17);
         } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
@@ -614,6 +615,20 @@
         assertFalse(nc2.appliesToUid(12));
         assertTrue(nc1.appliesToUid(22));
         assertTrue(nc2.appliesToUid(22));
+
+        // Verify the subscription id list can be combined only when they are equal.
+        if (isAtLeastS()) {
+            nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            nc2.setSubIds(Set.of(TEST_SUBID2));
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of());
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.combineCapabilities(nc1);
+            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+        }
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -762,6 +777,24 @@
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
         assertEquals(nc1, nc2);
+
+        if (isAtLeastS()) {
+            assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
+            nc1.setSubIds(Set.of());
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc1.setSubIds(Set.of(TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+            assertNotEquals(nc1, nc2);
+        }
     }
 
     @Test
@@ -842,6 +875,50 @@
         } catch (NullPointerException expected) { }
     }
 
+    private static NetworkCapabilities capsWithSubIds(Integer ... subIds) {
+        // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for
+        // every NetworkCapabilities that simulates networks needs to add it too in order to
+        // satisfy these requests.
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                .setSubIds(new ArraySet<>(subIds)).build();
+        assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+        return nc;
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testSubIds() throws Exception {
+        final NetworkCapabilities ncWithoutId = capsWithSubIds();
+        final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1);
+        final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3);
+        final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
+
+        final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
+        assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+        final NetworkRequest requestWithIds = new NetworkRequest.Builder()
+                .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+        assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
+                requestWithIds.networkCapabilities.getSubIds());
+
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId));
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testEqualsSubIds() throws Exception {
+        assertEquals(capsWithSubIds(), capsWithSubIds());
+        assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2),
+                capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+    }
+
     @Test
     public void testLinkBandwidthKbps() {
         final NetworkCapabilities nc = new NetworkCapabilities();
@@ -1022,5 +1099,11 @@
             fail("Should not set null into NetworkCapabilities.Builder");
         } catch (NullPointerException expected) { }
         assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+
+        if (isAtLeastS()) {
+            final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                    .setSubIds(Set.of(TEST_SUBID1)).build();
+            assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+        }
     }
 }
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 6a09b02..6fc605e 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -220,7 +220,7 @@
 
         // register callback
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(request);
+                anyInt(), any(), nullable(String.class))).thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
         // callback triggers
@@ -248,7 +248,7 @@
 
         // register callback
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req1);
+                anyInt(), any(), nullable(String.class))).thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
         // callback triggers
@@ -266,7 +266,7 @@
 
         // callback can be registered again
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req2);
+                anyInt(), any(), nullable(String.class))).thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
         // callback triggers
@@ -289,8 +289,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(),
-                nullable(String.class))).thenReturn(request);
+        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(),
+                any(), nullable(String.class))).thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
@@ -358,34 +358,34 @@
 
         manager.requestNetwork(request, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
         manager.registerNetworkCallback(request, callback);
-        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
+        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(),
                 anyInt(), any(), any());
-        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
+        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.registerDefaultNetworkCallback(callback);
         verify(mService).requestNetwork(eq(null),
-                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(testAttributionTag));
-        reset(mService);
-
-        manager.requestBackgroundNetwork(request, null, callback);
-        verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+        manager.requestBackgroundNetwork(request, handler, callback);
+        verify(mService).requestNetwork(eq(request.networkCapabilities),
+                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
+                eq(testPkgName), eq(testAttributionTag));
+        reset(mService);
+
         manager.registerSystemDefaultNetworkCallback(callback, handler);
         verify(mService).requestNetwork(eq(null),
-                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 058e639..fadd1ea 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -275,6 +275,7 @@
 import com.android.net.module.util.ArrayTrackRecord;
 import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
 import com.android.server.connectivity.ConnectivityConstants;
+import com.android.server.connectivity.ConnectivityResources;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
@@ -444,6 +445,7 @@
     @Mock NetworkPolicyManager mNetworkPolicyManager;
     @Mock VpnProfileStore mVpnProfileStore;
     @Mock SystemConfigManager mSystemConfigManager;
+    @Mock Resources mResources;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -471,7 +473,7 @@
     private class MockContext extends BroadcastInterceptingContext {
         private final MockContentResolver mContentResolver;
 
-        @Spy private Resources mResources;
+        @Spy private Resources mInternalResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
 
         // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
@@ -480,21 +482,15 @@
         MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
 
-            mResources = spy(base.getResources());
-            when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)).
-                    thenReturn(new String[] {
+            mInternalResources = spy(base.getResources());
+            when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
+                    .thenReturn(new String[] {
                             "wifi,1,1,1,-1,true",
                             "mobile,0,0,0,-1,true",
                             "mobile_mms,2,0,2,60000,true",
                             "mobile_supl,3,0,2,60000,true",
                     });
 
-            when(mResources.getStringArray(
-                    com.android.internal.R.array.config_wakeonlan_supported_interfaces))
-                    .thenReturn(new String[]{
-                            WIFI_WOL_IFNAME,
-                    });
-
             mContentResolver = new MockContentResolver();
             mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
@@ -559,7 +555,7 @@
 
         @Override
         public Resources getResources() {
-            return mResources;
+            return mInternalResources;
         }
 
         @Override
@@ -1454,6 +1450,8 @@
         applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(anyString()))
+                .thenReturn(applicationInfo.targetSdkVersion);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
 
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
@@ -1530,6 +1528,18 @@
                     inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
             return mPolicyTracker;
         }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+        doReturn(true).when(deps).getCellular464XlatEnabled();
+
+        doReturn(60000).when(mResources).getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        doReturn("").when(mResources).getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+        doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
+                com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces);
+        final com.android.server.connectivity.ConnectivityResources connRes = mock(
+                ConnectivityResources.class);
+        doReturn(mResources).when(connRes).get();
+        doReturn(connRes).when(deps).getResources(any());
 
         return deps;
     }
@@ -1740,11 +1750,29 @@
         return expected;
     }
 
+    private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) {
+        final DetailedState state = ni.getDetailedState();
+        if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false;
+        // Expect a null extraInfo if the network is CONNECTING, because a CONNECTIVITY_ACTION
+        // broadcast with a state of CONNECTING only happens due to legacy VPN lockdown, which also
+        // nulls out extraInfo.
+        if (state == DetailedState.CONNECTING && ni.getExtraInfo() != null) return false;
+        // Can't make any assertions about DISCONNECTED broadcasts. When a network actually
+        // disconnects, disconnectAndDestroyNetwork sets its state to DISCONNECTED and its extraInfo
+        // to null. But if the DISCONNECTED broadcast is just simulated by LegacyTypeTracker due to
+        // a network switch, extraInfo will likely be populated.
+        // This is likely a bug in CS, but likely not one we can fix without impacting apps.
+        return true;
+    }
+
     private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
-        return registerConnectivityBroadcastThat(1, intent ->
-                type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
-                        ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
-                                .getDetailedState()));
+        return registerConnectivityBroadcastThat(1, intent -> {
+            final int actualType = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+            final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+            return type == actualType
+                    && state == ni.getDetailedState()
+                    && extraInfoInBroadcastHasExpectedNullness(ni);
+        });
     }
 
     @Test
@@ -3730,8 +3758,8 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(),
-                    null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(),
-                    getAttributionTag());
+                    null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE,
+                    mContext.getPackageName(), getAttributionTag());
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -4008,7 +4036,8 @@
         grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
         final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
         mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+                .addTransportType(TRANSPORT_CELLULAR).build(),
+                mCsHandlerThread.getThreadHandler(), cellBgCallback);
 
         // Make callbacks for monitoring.
         final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -7182,12 +7211,14 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_REJECT_ALL);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // ConnectivityService should cache it not to invoke the callback again.
         setUidRulesChanged(RULE_REJECT_METERED);
@@ -7198,12 +7229,14 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_REJECT_METERED);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // Restrict the network based on UID rule and NOT_METERED capability change.
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -7212,6 +7245,7 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
         cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
@@ -7220,12 +7254,14 @@
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_ALLOW_METERED);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_NONE);
         cellNetworkCallback.assertNoCallback();
@@ -7236,6 +7272,7 @@
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
         setRestrictBackgroundChanged(true);
         cellNetworkCallback.assertNoCallback();
 
@@ -7243,12 +7280,14 @@
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setRestrictBackgroundChanged(false);
         cellNetworkCallback.assertNoCallback();
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         mCm.unregisterNetworkCallback(cellNetworkCallback);
     }
@@ -7307,6 +7346,15 @@
         assertNotNull(ni);
         assertEquals(type, ni.getType());
         assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+        if (state == DetailedState.CONNECTED || state == DetailedState.SUSPENDED) {
+            assertNotNull(ni.getExtraInfo());
+        } else {
+            // Technically speaking, a network that's in CONNECTING state will generally have a
+            // non-null extraInfo. This doesn't actually happen in this test because it never calls
+            // a legacy API while a network is connecting. When a network is in CONNECTING state
+            // because of legacy lockdown VPN, its extraInfo is always null.
+            assertNull(ni.getExtraInfo());
+        }
     }
 
     private void assertActiveNetworkInfo(int type, DetailedState state) {
@@ -7316,6 +7364,26 @@
         checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
     }
 
+    private void assertExtraInfoFromCm(TestNetworkAgentWrapper network, boolean present) {
+        final NetworkInfo niForNetwork = mCm.getNetworkInfo(network.getNetwork());
+        final NetworkInfo niForType = mCm.getNetworkInfo(network.getLegacyType());
+        if (present) {
+            assertEquals(network.getExtraInfo(), niForNetwork.getExtraInfo());
+            assertEquals(network.getExtraInfo(), niForType.getExtraInfo());
+        } else {
+            assertNull(niForNetwork.getExtraInfo());
+            assertNull(niForType.getExtraInfo());
+        }
+    }
+
+    private void assertExtraInfoFromCmBlocked(TestNetworkAgentWrapper network) {
+        assertExtraInfoFromCm(network, false);
+    }
+
+    private void assertExtraInfoFromCmPresent(TestNetworkAgentWrapper network) {
+        assertExtraInfoFromCm(network, true);
+    }
+
     // Checks that each of the |agents| receive a blocked status change callback with the specified
     // |blocked| value, in any order. This is needed because when an event affects multiple
     // networks, ConnectivityService does not guarantee the order in which callbacks are fired.
@@ -7630,6 +7698,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // TODO: it would be nice if we could simply rely on the production code here, and have
         // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
@@ -7658,6 +7727,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
         assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7700,6 +7770,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mWiFiNetworkAgent);
 
         // The VPN comes up again on wifi.
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
@@ -7714,6 +7785,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
         vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
         assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7730,6 +7802,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
 
         b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
         mWiFiNetworkAgent.disconnect();
@@ -8262,6 +8335,45 @@
     }
 
     @Test
+    public void testWith464XlatDisable() throws Exception {
+        doReturn(false).when(mDeps).getCellular464XlatEnabled();
+
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCm.registerNetworkCallback(networkRequest, callback);
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        // Bring up validated cell.
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+        cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, MOBILE_IFNAME));
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+        waitForIdle();
+
+        verify(mMockDnsResolver, never()).startPrefix64Discovery(cellNetId);
+        Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
+        assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+
+        // This cannot happen because prefix discovery cannot succeed if it is never started.
+        mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+                makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, "64:ff9b::", 96));
+
+        // ... but still, check that even if it did, clatd would not be started.
+        verify(mMockNetd, never()).clatdStart(anyString(), anyString());
+        assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+    }
+
+    @Test
     public void testDataActivityTracking() throws Exception {
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
         final NetworkRequest networkRequest = new NetworkRequest.Builder()
@@ -8653,6 +8765,7 @@
         applicationInfo.targetSdkVersion = targetSdk;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
 
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
@@ -8667,102 +8780,183 @@
         }
     }
 
-    private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+    private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid,
+            boolean includeLocationSensitiveInfo) {
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
+                netCap, includeLocationSensitiveInfo, callerUid,
+                mContext.getPackageName(), getAttributionTag())
+                .getOwnerUid();
     }
 
-    private void verifyWifiInfoCopyNetCapsForCallerPermission(
-            int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+    private void verifyWifiInfoCopyNetCapsPermission(
+            int callerUid, boolean includeLocationSensitiveInfo,
+            boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
         final WifiInfo wifiInfo = mock(WifiInfo.class);
         when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
         final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
+                netCap, includeLocationSensitiveInfo, callerUid,
+                mContext.getPackageName(), getAttributionTag());
         verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
     }
 
+    private void verifyOwnerUidAndWifiInfoNetCapsPermission(
+            boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag,
+            boolean shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveWifiInfoWithIncludeFlag) {
+        final int myUid = Process.myUid();
+
+        final int expectedOwnerUidWithoutIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
+                        ? Process.myUid() : INVALID_UID;
+        assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, false /* includeLocationSensitiveInfo */));
+
+        final int expectedOwnerUidWithIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID;
+        assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, true /* includeLocationSensitiveInfo */));
+
+        verifyWifiInfoCopyNetCapsPermission(myUid,
+                false, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag);
+
+        verifyWifiInfoCopyNetCapsPermission(myUid,
+                true, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveWifiInfoWithIncludeFlag);
+
+    }
+
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void
+            testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception {
+    public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception {
         // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
         setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception {
+    public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception {
         // Test that even with fine location permission, not being the owner leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
         final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        assertEquals(Process.INVALID_UID,
+                getOwnerUidNetCapsPermission(myUid + 1, myUid,
+                        true /* includeLocationSensitiveInfo */));
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ()
             throws Exception {
         // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission()
+    public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
             throws Exception {
+        // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8967,7 +9161,7 @@
                 TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
         return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
                 nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
-                INVALID_UID, mQosCallbackTracker);
+                INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies());
     }
 
     @Test
@@ -9353,8 +9547,8 @@
             assertThrows("Expect throws for invalid request type " + reqTypeInt,
                     IllegalArgumentException.class,
                     () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null,
-                            ConnectivityManager.TYPE_NONE, mContext.getPackageName(),
-                            getAttributionTag())
+                            ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE,
+                            mContext.getPackageName(), getAttributionTag())
             );
         }
     }
@@ -10924,4 +11118,12 @@
         verifyNoNetwork();
         mCm.unregisterNetworkCallback(cellCb);
     }
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        assertThrows(UnsupportedOperationException.class,
+                () -> mCm.registerBestMatchingNetworkCallback(request, new NetworkCallback(),
+                        mCsHandlerThread.getThreadHandler()));
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index f97eabf..6232423 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecConfig;
@@ -47,6 +48,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
+import android.util.Range;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -647,9 +649,9 @@
 
     @Test
     public void testReserveNetId() {
-        int start = mIpSecService.TUN_INTF_NETID_START;
-        for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
-            assertEquals(start + i, mIpSecService.reserveNetId());
+        final Range<Integer> netIdRange = ConnectivityManager.getIpSecNetIdRange();
+        for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) {
+            assertEquals(netId, mIpSecService.reserveNetId());
         }
 
         // Check that resource exhaustion triggers an exception
@@ -661,7 +663,7 @@
 
         // Now release one and try again
         int releasedNetId =
-                mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+                netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2;
         mIpSecService.releaseNetId(releasedNetId);
         assertEquals(releasedNetId, mIpSecService.reserveNetId());
     }
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index a913673..1c0ba4f 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -357,7 +357,7 @@
         NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
                 new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
                 mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
-                mQosCallbackTracker);
+                mQosCallbackTracker, new ConnectivityService.Dependencies());
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 5f56e25..9b2a638 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -16,11 +16,15 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -34,6 +38,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.test.TestLooper;
@@ -72,11 +77,15 @@
     Handler mHandler;
     NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
 
-    Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNai, mNetd, mDnsResolver) {
+    Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
+        return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) {
             @Override protected int getNetId() {
                 return NETID;
             }
+
+            @Override protected boolean isCellular464XlatEnabled() {
+                return isCellular464XlatEnabled;
+            }
         };
     }
 
@@ -99,6 +108,7 @@
         mNai.linkProperties.setInterfaceName(BASE_IFACE);
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+        mNai.networkCapabilities = new NetworkCapabilities();
         markNetworkConnected();
         when(mNai.connService()).thenReturn(mConnectivity);
         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
@@ -110,21 +120,23 @@
     }
 
     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
+        Nat464Xlat nat = makeNat464Xlat(true);
         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
-        assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
+        assertEquals(msg, expected, nat.requiresClat(nai));
     }
 
     private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
+        Nat464Xlat nat = makeNat464Xlat(true);
         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
-        assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
+        assertEquals(msg, expected, nat.shouldStartClat(nai));
     }
 
     @Test
@@ -194,7 +206,7 @@
     }
 
     private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
         mNai.linkProperties.addLinkAddress(V6ADDR);
@@ -245,7 +257,7 @@
     }
 
     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mConnectivity);
 
@@ -335,7 +347,7 @@
 
     @Test
     public void testClatdCrashWhileRunning() throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
@@ -372,7 +384,7 @@
     }
 
     private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
 
@@ -414,7 +426,7 @@
     }
 
     private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
 
@@ -450,7 +462,7 @@
         final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
         final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
 
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         final LinkProperties emptyLp = new LinkProperties();
         LinkProperties fixedupLp;
@@ -486,10 +498,57 @@
         assertEquals(null, fixedupLp.getNat64Prefix());
     }
 
+    private void checkClatDisabledOnCellular(boolean onCellular) throws Exception {
+        // Disable 464xlat on cellular networks.
+        Nat464Xlat nat = makeNat464Xlat(false);
+        mNai.linkProperties.addLinkAddress(V6ADDR);
+        mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular);
+        nat.update();
+
+        final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX);
+        if (onCellular) {
+            // Prefix discovery is never started.
+            verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID));
+            assertIdle(nat);
+
+            // If a NAT64 prefix comes in from an RA, clat is not started either.
+            mNai.linkProperties.setNat64Prefix(nat64Prefix);
+            nat.setNat64PrefixFromRa(nat64Prefix);
+            nat.update();
+            verify(mNetd, never()).clatdStart(anyString(), anyString());
+            assertIdle(nat);
+        } else {
+            // Prefix discovery is started.
+            verify(mDnsResolver).startPrefix64Discovery(eq(NETID));
+            assertIdle(nat);
+
+            // If a NAT64 prefix comes in from an RA, clat is started.
+            mNai.linkProperties.setNat64Prefix(nat64Prefix);
+            nat.setNat64PrefixFromRa(nat64Prefix);
+            nat.update();
+            verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX);
+            assertStarting(nat);
+        }
+    }
+
+    @Test
+    public void testClatDisabledOnCellular() throws Exception {
+        checkClatDisabledOnCellular(true);
+    }
+
+    @Test
+    public void testClatDisabledOnNonCellular() throws Exception {
+        checkClatDisabledOnCellular(false);
+    }
+
     static void assertIdle(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
     }
 
+    static void assertStarting(Nat464Xlat nat) {
+        assertTrue("Nat464Xlat was not STARTING", nat.isStarting());
+    }
+
     static void assertRunning(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
     }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index e4e24b4..fec5ef3 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -48,18 +48,22 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.SystemConfigManager;
 import android.os.UserHandle;
@@ -70,12 +74,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.LocalServices;
-import com.android.server.pm.PackageList;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -112,7 +115,6 @@
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
     @Mock private INetd mNetdService;
-    @Mock private PackageManagerInternal mMockPmi;
     @Mock private UserManager mUserManager;
     @Mock private PermissionMonitor.Dependencies mDeps;
     @Mock private SystemConfigManager mSystemConfigManager;
@@ -131,16 +133,14 @@
         when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
                 .thenReturn(mSystemConfigManager);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+        final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+        doReturn(UserHandle.ALL).when(asUserCtx).getUser();
+        when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
 
         mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mMockPmi);
-        when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(),
-                  /* observer */ null));
         when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
         mPermissionMonitor.startMonitoring();
-        verify(mMockPmi).getPackageList(mPermissionMonitor);
     }
 
     private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
@@ -770,4 +770,32 @@
                 INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
                 new int[]{ MOCK_UID2 });
     }
+
+    @Test
+    public void testIntentReceiver() throws Exception {
+        final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+        final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
+        final BroadcastReceiver receiver = receiverCaptor.getValue();
+
+        // Verify receiving PACKAGE_ADDED intent.
+        final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1,
+                new String[] { INTERNET, UPDATE_DEVICE_STATS });
+        receiver.onReceive(mContext, addedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 });
+
+        // Verify receiving PACKAGE_REMOVED intent.
+        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null);
+        final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        receiver.onReceive(mContext, removedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 });
+    }
+
 }
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a07bce3..8932892 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -30,12 +30,21 @@
  *     -e selectTest_verbose true \
  *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  * </pre>
+ *
+ * <p>Use this filter when running FrameworksMockingCoreTests as
+ * <pre>
+ * adb shell am instrument -w \
+ *     -e filter com.android.server.wm.test.filters.FrameworksTestsFilter \
+ *     -e selectTest_verbose true \
+ *     com.android.frameworks.mockingcoretests/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
  */
 public final class FrameworksTestsFilter extends SelectTest {
 
     private static final String[] SELECTED_TESTS = {
             // Test specifications for FrameworksMockingCoreTests.
             "android.app.activity.ActivityThreadClientTest",
+            "android.view.DisplayTest",
             // Test specifications for FrameworksCoreTests.
             "android.app.servertransaction.", // all tests under the package.
             "android.view.CutoutSpecificationTest",
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 11498de..a0200275 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -593,6 +593,16 @@
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testRemoveVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+        doThrow(new SecurityException())
+                .when(mMockContext)
+                .enforceCallingOrSelfPermission(
+                        eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+        mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+    }
+
     @Test
     public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);