Merge "Fix flakiness of testTaskChangeCallBacks"
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 2320b75..ebb0070 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -40,30 +40,77 @@
   public final class AppSearchSchema {
     method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
     method @NonNull public String getSchemaType();
+    method @IntRange(from=0) public int getVersion();
+  }
+
+  public static final class AppSearchSchema.BooleanPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.BooleanPropertyConfig.Builder {
+    ctor public AppSearchSchema.BooleanPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig.Builder setCardinality(int);
   }
 
   public static final class AppSearchSchema.Builder {
     ctor public AppSearchSchema.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
     method @NonNull public android.app.appsearch.AppSearchSchema build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.Builder setVersion(@IntRange(from=0) int);
   }
 
-  public static final class AppSearchSchema.PropertyConfig {
+  public static final class AppSearchSchema.BytesPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.BytesPropertyConfig.Builder {
+    ctor public AppSearchSchema.BytesPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig.Builder setCardinality(int);
+  }
+
+  public static final class AppSearchSchema.DocumentPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+    method @NonNull public String getSchemaType();
+    method public boolean isIndexNestedProperties();
+  }
+
+  public static final class AppSearchSchema.DocumentPropertyConfig.Builder {
+    ctor public AppSearchSchema.DocumentPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setIndexNestedProperties(boolean);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setSchemaType(@NonNull String);
+  }
+
+  public static final class AppSearchSchema.DoublePropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.DoublePropertyConfig.Builder {
+    ctor public AppSearchSchema.DoublePropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int);
+  }
+
+  public static final class AppSearchSchema.Int64PropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.Int64PropertyConfig.Builder {
+    ctor public AppSearchSchema.Int64PropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig.Builder setCardinality(int);
+  }
+
+  public abstract static class AppSearchSchema.PropertyConfig {
     method public int getCardinality();
     method public int getDataType();
-    method public int getIndexingType();
     method @NonNull public String getName();
-    method @Nullable public String getSchemaType();
-    method public int getTokenizerType();
     field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
     field public static final int CARDINALITY_REPEATED = 1; // 0x1
     field public static final int CARDINALITY_REQUIRED = 3; // 0x3
-    field public static final int DATA_TYPE_BOOLEAN = 4; // 0x4
-    field public static final int DATA_TYPE_BYTES = 5; // 0x5
-    field public static final int DATA_TYPE_DOCUMENT = 6; // 0x6
-    field public static final int DATA_TYPE_DOUBLE = 3; // 0x3
-    field public static final int DATA_TYPE_INT64 = 2; // 0x2
-    field public static final int DATA_TYPE_STRING = 1; // 0x1
+  }
+
+  public static final class AppSearchSchema.StringPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+    method public int getIndexingType();
+    method public int getTokenizerType();
     field public static final int INDEXING_TYPE_EXACT_TERMS = 1; // 0x1
     field public static final int INDEXING_TYPE_NONE = 0; // 0x0
     field public static final int INDEXING_TYPE_PREFIXES = 2; // 0x2
@@ -71,14 +118,12 @@
     field public static final int TOKENIZER_TYPE_PLAIN = 1; // 0x1
   }
 
-  public static final class AppSearchSchema.PropertyConfig.Builder {
-    ctor public AppSearchSchema.PropertyConfig.Builder(@NonNull String);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig build();
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setCardinality(int);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setDataType(int);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setIndexingType(int);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setSchemaType(@NonNull String);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setTokenizerType(int);
+  public static final class AppSearchSchema.StringPropertyConfig.Builder {
+    ctor public AppSearchSchema.StringPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
   }
 
   public final class AppSearchSession implements java.io.Closeable {
@@ -89,6 +134,7 @@
     method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
     method public void removeByQuery(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
     method public void removeByUri(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
+    method @NonNull public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
     method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
   }
 
@@ -140,11 +186,15 @@
 
   public final class GetByUriRequest {
     method @NonNull public String getNamespace();
+    method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
     method @NonNull public java.util.Set<java.lang.String> getUris();
+    field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
   }
 
   public static final class GetByUriRequest.Builder {
     ctor public GetByUriRequest.Builder();
+    method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
+    method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest build();
@@ -186,7 +236,22 @@
     method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
   }
 
+  public final class ReportUsageRequest {
+    method @NonNull public String getNamespace();
+    method @NonNull public String getUri();
+    method public long getUsageTimeMillis();
+  }
+
+  public static final class ReportUsageRequest.Builder {
+    ctor public ReportUsageRequest.Builder();
+    method @NonNull public android.app.appsearch.ReportUsageRequest build();
+    method @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 @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
     method @NonNull public String getPackageName();
@@ -230,6 +295,8 @@
     field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
     field public static final int RANKING_STRATEGY_NONE = 0; // 0x0
     field public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; // 0x3
+    field public static final int RANKING_STRATEGY_USAGE_COUNT = 4; // 0x4
+    field public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; // 0x5
     field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
     field public static final int TERM_MATCH_PREFIX = 2; // 0x2
   }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 6fa8f85..814800e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -46,6 +46,7 @@
 public class AppSearchManager {
     /**
      * The default empty database name.
+     *
      * @hide
      */
     public static final String DEFAULT_DATABASE_NAME = "";
@@ -70,8 +71,8 @@
         /**
          * Returns the name of the database to create or open.
          *
-         * <p>Databases with different names are fully separate with distinct types, namespaces,
-         * and data.
+         * <p>Databases with different names are fully separate with distinct types, namespaces, and
+         * data.
          */
         @NonNull
         public String getDatabaseName() {
@@ -126,11 +127,11 @@
      * initialization process will create one under the user's credential encrypted directory.
      *
      * @param searchContext The {@link SearchContext} contains all information to create a new
-     *                      {@link AppSearchSession}
-     * @param executor      Executor on which to invoke the callback.
-     * @param callback      The {@link AppSearchResult}&lt;{@link AppSearchSession}&gt; of
-     *                      performing this operation. Or a {@link AppSearchResult} with failure
-     *                      reason code and error information.
+     *     {@link AppSearchSession}
+     * @param executor Executor on which to invoke the callback.
+     * @param callback The {@link AppSearchResult}&lt;{@link AppSearchSession}&gt; of performing
+     *     this operation. Or a {@link AppSearchResult} with failure reason code and error
+     *     information.
      */
     public void createSearchSession(
             @NonNull SearchContext searchContext,
@@ -140,7 +141,12 @@
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
         AppSearchSession.createSearchSession(
-                searchContext, mService, mContext.getUserId(), executor, callback);
+                searchContext,
+                mService,
+                mContext.getUserId(),
+                getPackageName(),
+                executor,
+                callback);
     }
 
     /**
@@ -149,10 +155,10 @@
      * <p>This process requires an AppSearch native indexing file system. If it's not created, the
      * initialization process will create one under the user's credential encrypted directory.
      *
-     * @param executor      Executor on which to invoke the callback.
-     * @param callback      The {@link AppSearchResult}&lt;{@link GlobalSearchSession}&gt; of
-     *                      performing this operation. Or a {@link AppSearchResult} with failure
-     *                      reason code and error information.
+     * @param executor Executor on which to invoke the callback.
+     * @param callback The {@link AppSearchResult}&lt;{@link GlobalSearchSession}&gt; of performing
+     *     this operation. Or a {@link AppSearchResult} with failure reason code and error
+     *     information.
      */
     public void createGlobalSearchSession(
             @NonNull @CallbackExecutor Executor executor,
@@ -160,7 +166,7 @@
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
         GlobalSearchSession.createGlobalSearchSession(
-                mService, mContext.getUserId(), executor, callback);
+                mService, mContext.getUserId(), getPackageName(), executor, callback);
     }
 
     /**
@@ -170,40 +176,42 @@
      * 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.
+     *   <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.
+     *   <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>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
@@ -214,8 +222,8 @@
      *
      * @param request The schema update request.
      * @return the result of performing this operation.
-     * @deprecated use {@link AppSearchSession#setSchema} instead.
      * @hide
+     * @deprecated use {@link AppSearchSession#setSchema} instead.
      */
     @NonNull
     public AppSearchResult<Void> setSchema(@NonNull SetSchemaRequest request) {
@@ -229,6 +237,7 @@
         AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
             mService.setSchema(
+                    getPackageName(),
                     DEFAULT_DATABASE_NAME,
                     schemaBundles,
                     new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
@@ -249,21 +258,19 @@
     /**
      * 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>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.
+     * @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.
-     *
-     * @deprecated use {@link AppSearchSession#putDocuments} instead.
      * @hide
+     * @deprecated use {@link AppSearchSession#putDocuments} instead.
      */
     public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
         // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
@@ -275,7 +282,11 @@
         }
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.putDocuments(DEFAULT_DATABASE_NAME, documentBundles, mContext.getUserId(),
+            mService.putDocuments(
+                    getPackageName(),
+                    DEFAULT_DATABASE_NAME,
+                    documentBundles,
+                    mContext.getUserId(),
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
                             future.complete(result);
@@ -294,19 +305,18 @@
     /**
      * Retrieves {@link GenericDocument}s by URI.
      *
-     * <p>You should not call this method directly; instead, use the
-     * {@code AppSearch#getDocuments()} API provided by JetPack.
+     * <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}.
+     * @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.
-     *
-     * @deprecated use {@link AppSearchSession#getByUri} instead.
      * @hide
+     * @deprecated use {@link AppSearchSession#getByUri} instead.
      */
     public AppSearchBatchResult<String, GenericDocument> getByUri(
             @NonNull GetByUriRequest request) {
@@ -315,7 +325,12 @@
         List<String> uris = new ArrayList<>(request.getUris());
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.getDocuments(DEFAULT_DATABASE_NAME, request.getNamespace(), uris,
+            mService.getDocuments(
+                    getPackageName(),
+                    DEFAULT_DATABASE_NAME,
+                    request.getNamespace(),
+                    uris,
+                    request.getProjectionsVisibleToPackagesInternal(),
                     mContext.getUserId(),
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
@@ -371,43 +386,39 @@
      * 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.
+     *   <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.
-     *
-     * @deprecated use AppSearchSession#query instead.
      * @hide
+     * @deprecated use AppSearchSession#query instead.
      */
     @NonNull
     public AppSearchResult<List<SearchResult>> query(
@@ -416,7 +427,11 @@
         //     them in one big list.
         AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
-            mService.query(DEFAULT_DATABASE_NAME, queryExpression, searchSpec.getBundle(),
+            mService.query(
+                    getPackageName(),
+                    DEFAULT_DATABASE_NAME,
+                    queryExpression,
+                    searchSpec.getBundle(),
                     mContext.getUserId(),
                     new IAppSearchResultCallback.Stub() {
                         public void onResult(AppSearchResult result) {
@@ -425,8 +440,8 @@
                     });
             AppSearchResult<Bundle> bundleResult = getFutureOrThrow(future);
             if (!bundleResult.isSuccess()) {
-                return AppSearchResult.newFailedResult(bundleResult.getResultCode(),
-                        bundleResult.getErrorMessage());
+                return AppSearchResult.newFailedResult(
+                        bundleResult.getResultCode(), bundleResult.getErrorMessage());
             }
             SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue());
             return AppSearchResult.newSuccessfulResult(searchResultPage.getResults());
@@ -444,21 +459,23 @@
      * 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}.
+     * @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.
-     *
-     * @deprecated use {@link AppSearchSession#removeByUri} instead.
      * @hide
+     * @deprecated use {@link AppSearchSession#removeByUri} instead.
      */
     public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
         List<String> uris = new ArrayList<>(request.getUris());
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.removeByUri(DEFAULT_DATABASE_NAME, request.getNamespace(), uris,
+            mService.removeByUri(
+                    getPackageName(),
+                    DEFAULT_DATABASE_NAME,
+                    request.getNamespace(),
+                    uris,
                     mContext.getUserId(),
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
@@ -475,6 +492,12 @@
         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();
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 0427577..c4c123c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -45,6 +45,7 @@
  */
 public final class AppSearchSession implements Closeable {
     private static final String TAG = "AppSearchSession";
+    private final String mPackageName;
     private final String mDatabaseName;
     @UserIdInt
     private final int mUserId;
@@ -52,14 +53,20 @@
     private boolean mIsMutated = false;
     private boolean mIsClosed = false;
 
+
+    /**
+     * Creates a search session for the client, defined by the {@code userId} and
+     * {@code packageName}.
+     */
     static void createSearchSession(
             @NonNull AppSearchManager.SearchContext searchContext,
             @NonNull IAppSearchManager service,
             @UserIdInt int userId,
+            @NonNull String packageName,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
         AppSearchSession searchSession =
-                new AppSearchSession(service, userId, searchContext.mDatabaseName);
+                new AppSearchSession(service, userId, packageName, searchContext.mDatabaseName);
         searchSession.initialize(executor, callback);
     }
 
@@ -87,10 +94,11 @@
     }
 
     private AppSearchSession(@NonNull IAppSearchManager service, @UserIdInt int userId,
-            @NonNull String databaseName) {
-        mDatabaseName = databaseName;
+            @NonNull String packageName, @NonNull String databaseName) {
         mService = service;
         mUserId = userId;
+        mPackageName = packageName;
+        mDatabaseName = databaseName;
     }
 
     /**
@@ -144,7 +152,7 @@
      * Visibility settings for a schema type do not apply or persist across
      * {@link SetSchemaRequest}s.
      *
-     * @param request The schema update request.
+     * @param request  The schema update request.
      * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive errors resulting from setting the schema. If the
      *                 operation succeeds, the callback will be invoked with {@code null}.
@@ -175,6 +183,7 @@
         }
         try {
             mService.setSchema(
+                    mPackageName,
                     mDatabaseName,
                     schemaBundles,
                     new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
@@ -206,6 +215,7 @@
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
             mService.getSchema(
+                    mPackageName,
                     mDatabaseName,
                     mUserId,
                     new IAppSearchResultCallback.Stub() {
@@ -261,7 +271,7 @@
             documentBundles.add(documents.get(i).getBundle());
         }
         try {
-            mService.putDocuments(mDatabaseName, documentBundles, mUserId,
+            mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
                             executor.execute(() -> callback.onResult(result));
@@ -280,7 +290,7 @@
     /**
      * Retrieves {@link GenericDocument}s by URI.
      *
-     * @param request {@link GetByUriRequest} containing URIs to be retrieved.
+     * @param request  {@link GetByUriRequest} containing URIs to be retrieved.
      * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive the pending result of performing this operation. The keys
      *                 of the returned {@link AppSearchBatchResult} are the input URIs. The values
@@ -301,8 +311,13 @@
         Objects.requireNonNull(callback);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
-            mService.getDocuments(mDatabaseName, request.getNamespace(),
-                    new ArrayList<>(request.getUris()), mUserId,
+            mService.getDocuments(
+                    mPackageName,
+                    mDatabaseName,
+                    request.getNamespace(),
+                    new ArrayList<>(request.getUris()),
+                    request.getProjectionsVisibleToPackagesInternal(),
+                    mUserId,
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
                             executor.execute(() -> {
@@ -405,14 +420,59 @@
         Objects.requireNonNull(searchSpec);
         Objects.requireNonNull(executor);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
-        return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, mUserId,
-                executor);
+        return new SearchResults(mService, mPackageName, mDatabaseName, queryExpression,
+                searchSpec, mUserId, executor);
+    }
+
+    /**
+     * Reports usage of a particular document by URI and namespace.
+     *
+     * <p>A usage report represents an event in which a user interacted with or viewed a document.
+     *
+     * <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
+     * metrics for that particular document. These metrics are used for ordering {@link #query}
+     * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
+     * {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
+     *
+     * <p>Reporting usage of a document is optional.
+     *
+     * @param request The usage reporting request.
+     * @param executor Executor on which to invoke the callback.
+     * @param callback Callback to receive errors. If the operation succeeds, the callback will be
+     *                 invoked with {@code null}.
+     */
+    @NonNull
+    public void reportUsage(
+            @NonNull ReportUsageRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<Void>> callback) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
+        try {
+            mService.reportUsage(
+                    mPackageName,
+                    mDatabaseName,
+                    request.getNamespace(),
+                    request.getUri(),
+                    request.getUsageTimeMillis(),
+                    mUserId,
+                    new IAppSearchResultCallback.Stub() {
+                        public void onResult(AppSearchResult result) {
+                            executor.execute(() -> callback.accept(result));
+                        }
+                    });
+            mIsMutated = true;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * Removes {@link GenericDocument}s from the index by URI.
      *
-     * @param request Request containing URIs to be removed.
+     * @param request  Request containing URIs to be removed.
      * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive the pending result of performing this operation. The keys
      *                 of the returned {@link AppSearchBatchResult} are the input URIs. The values
@@ -432,7 +492,7 @@
         Objects.requireNonNull(callback);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
-            mService.removeByUri(mDatabaseName, request.getNamespace(),
+            mService.removeByUri(mPackageName, mDatabaseName, request.getNamespace(),
                     new ArrayList<>(request.getUris()), mUserId,
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
@@ -478,7 +538,8 @@
         Objects.requireNonNull(callback);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
-            mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), mUserId,
+            mService.removeByQuery(mPackageName, mDatabaseName, queryExpression,
+                    searchSpec.getBundle(), mUserId,
                     new IAppSearchResultCallback.Stub() {
                         public void onResult(AppSearchResult result) {
                             executor.execute(() -> callback.accept(result));
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index e4e030e..6bb8554 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -37,16 +37,25 @@
 public class GlobalSearchSession implements Closeable {
 
     private final IAppSearchManager mService;
+
     @UserIdInt
     private final int mUserId;
     private boolean mIsClosed = false;
 
+    private final String mPackageName;
+
+    /**
+     * Creates a search session for the client, defined by the {@code userId} and
+     * {@code packageName}.
+     */
     static void createGlobalSearchSession(
             @NonNull IAppSearchManager service,
             @UserIdInt int userId,
+            @NonNull String packageName,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
-        GlobalSearchSession globalSearchSession = new GlobalSearchSession(service, userId);
+        GlobalSearchSession globalSearchSession = new GlobalSearchSession(service, userId,
+                packageName);
         globalSearchSession.initialize(executor, callback);
     }
 
@@ -73,9 +82,11 @@
         }
     }
 
-    private GlobalSearchSession(@NonNull IAppSearchManager service, @UserIdInt int userId) {
+    private GlobalSearchSession(@NonNull IAppSearchManager service, @UserIdInt int userId,
+            @NonNull String packageName) {
         mService = service;
         mUserId = userId;
+        mPackageName = packageName;
     }
 
     /**
@@ -131,7 +142,7 @@
         Objects.requireNonNull(searchSpec);
         Objects.requireNonNull(executor);
         Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed");
-        return new SearchResults(mService, /*databaseName=*/null, queryExpression,
+        return new SearchResults(mService, mPackageName, /*databaseName=*/null, queryExpression,
                 searchSpec, mUserId, executor);
     }
 
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 2b43777..68ae23f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -30,6 +30,7 @@
     /**
      * Updates the AppSearch schema for this database.
      *
+     * @param packageName The name of the package that owns this schema.
      * @param databaseName  The name of the database where this schema lives.
      * @param schemaBundles List of {@link AppSearchSchema} bundles.
      * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
@@ -43,6 +44,7 @@
      *     {@link AppSearchResult}&lt;{@link Void}&gt;.
      */
     void setSchema(
+        in String packageName,
         in String databaseName,
         in List<Bundle> schemaBundles,
         in List<String> schemasNotPlatformSurfaceable,
@@ -54,17 +56,23 @@
     /**
      * Retrieves the AppSearch schema for this database.
      *
+     * @param packageName The name of the package that owns the schema.
      * @param databaseName  The name of the database to retrieve.
      * @param userId Id of the calling user
      * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
      *     {@link AppSearchResult}&lt;{@link List}&lt;{@link Bundle}&gt;&gt;, where the value are
      *     AppSearchSchema bundle.
      */
-    void getSchema(in String databaseName, in int userId, in IAppSearchResultCallback callback);
+    void getSchema(
+        in String packageName,
+        in String databaseName,
+        in int userId,
+        in IAppSearchResultCallback callback);
 
     /**
      * Inserts documents into the index.
      *
+     * @param packageName The name of the package that owns this document.
      * @param databaseName  The name of the database where this document lives.
      * @param documentBundes List of GenericDocument bundles.
      * @param userId Id of the calling user
@@ -76,6 +84,7 @@
      *     where the keys are document URIs, and the values are {@code null}.
      */
     void putDocuments(
+        in String packageName,
         in String databaseName,
         in List<Bundle> documentBundles,
         in int userId,
@@ -84,9 +93,12 @@
     /**
      * Retrieves documents from the index.
      *
+     * @param packageName The name of the package that owns this document.
      * @param databaseName  The databaseName this document resides in.
      * @param namespace    The namespace this document resides in.
      * @param uris The URIs of the documents to retrieve
+     * @param typePropertyPaths A map of schema type to a list of property paths to return in the
+     *     result.
      * @param userId Id of the calling user
      * @param callback
      *     If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError}
@@ -96,15 +108,18 @@
      *     where the keys are document URIs, and the values are Document bundles.
      */
     void getDocuments(
+        in String packageName,
         in String databaseName,
         in String namespace,
         in List<String> uris,
+        in Map<String, List<String>> typePropertyPaths,
         in int userId,
         in IAppSearchBatchResultCallback callback);
 
     /**
      * Searches a document based on a given specifications.
      *
+     * @param packageName The name of the package to query over.
      * @param databaseName The databaseName this query for.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
@@ -113,6 +128,7 @@
      *         operation.
      */
     void query(
+        in String packageName,
         in String databaseName,
         in String queryExpression,
         in Bundle searchSpecBundle,
@@ -123,6 +139,7 @@
      * Executes a global query, i.e. over all permitted databases, against the AppSearch index and
      * returns results.
      *
+     * @param packageName The name of the package making the query.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
      * @param userId Id of the calling user
@@ -130,6 +147,7 @@
      *         operation.
      */
     void globalQuery(
+        in String packageName,
         in String queryExpression,
         in Bundle searchSpecBundle,
         in int userId,
@@ -156,8 +174,39 @@
     void invalidateNextPageToken(in long nextPageToken, in int userId);
 
     /**
+     * Reports usage of a particular document by URI and namespace.
+     *
+     * <p>A usage report represents an event in which a user interacted with or viewed a document.
+     *
+     * <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
+     * metrics for that particular document. These metrics are used for ordering {@link #query}
+     * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
+     * {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
+     *
+     * <p>Reporting usage of a document is optional.
+     *
+     * @param packageName The name of the package that owns this document.
+     * @param databaseName  The name of the database to report usage against.
+     * @param namespace Namespace the document being used belongs to.
+     * @param uri URI of the document being used.
+     * @param usageTimeMillis The timestamp at which the document was used.
+     * @param userId Id of the calling user
+     * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+     *     {@link AppSearchResult}&lt;{@link Void}&gt;.
+     */
+     void reportUsage(
+         in String packageName,
+         in String databaseName,
+         in String namespace,
+         in String uri,
+         in long usageTimeMillis,
+         in int userId,
+         in IAppSearchResultCallback callback);
+
+    /**
      * Removes documents by URI.
      *
+     * @param packageName The name of the package the document is in.
      * @param databaseName The databaseName the document is in.
      * @param namespace    Namespace of the document to remove.
      * @param uris The URIs of the documents to delete
@@ -171,6 +220,7 @@
      *     failure where the {@code throwable} is {@code null}.
      */
     void removeByUri(
+        in String packageName,
         in String databaseName,
         in String namespace,
         in List<String> uris,
@@ -180,6 +230,7 @@
     /**
      * Removes documents by given query.
      *
+     * @param packageName The name of the package to query over.
      * @param databaseName The databaseName this query for.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
@@ -188,6 +239,7 @@
      *     {@link AppSearchResult}&lt;{@link Void}&gt;.
      */
     void removeByQuery(
+        in String packageName,
         in String databaseName,
         in String queryExpression,
         in Bundle searchSpecBundle,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 0cb0ea4..704509b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -47,6 +47,9 @@
 
     private final IAppSearchManager mService;
 
+    // The package name of the caller.
+    private final String mPackageName;
+
     // The database name to search over. If null, this will search over all database names.
     @Nullable
     private final String mDatabaseName;
@@ -68,12 +71,14 @@
 
     SearchResults(
             @NonNull IAppSearchManager service,
+            @NonNull String packageName,
             @Nullable String databaseName,
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec,
             @UserIdInt int userId,
             @NonNull @CallbackExecutor Executor executor) {
         mService = Objects.requireNonNull(service);
+        mPackageName = packageName;
         mDatabaseName = databaseName;
         mQueryExpression = Objects.requireNonNull(queryExpression);
         mSearchSpec = Objects.requireNonNull(searchSpec);
@@ -98,13 +103,12 @@
                 mIsFirstLoad = false;
                 if (mDatabaseName == null) {
                     // Global query, there's no one package-database combination to check.
-                    mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(), mUserId,
-                            wrapCallback(callback));
+                    mService.globalQuery(mPackageName, mQueryExpression,
+                            mSearchSpec.getBundle(), mUserId, wrapCallback(callback));
                 } else {
                     // Normal local query, pass in specified database.
-                    mService.query(
-                            mDatabaseName, mQueryExpression, mSearchSpec.getBundle(), mUserId,
-                            wrapCallback(callback));
+                    mService.query(mPackageName, mDatabaseName, mQueryExpression,
+                            mSearchSpec.getBundle(), mUserId, wrapCallback(callback));
                 }
             } else {
                 mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback));
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 9ca363e..d394904 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.appsearch.AppSearchSchema.PropertyConfig;
+import android.app.appsearch.AppSearchSchema.StringPropertyConfig;
 
 /**
  * Encapsulates a {@link GenericDocument} that represent an email.
@@ -41,46 +42,40 @@
     public static final AppSearchSchema SCHEMA =
             new AppSearchSchema.Builder(SCHEMA_TYPE)
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_FROM)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_FROM)
                                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_TO)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_TO)
                                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_CC)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_CC)
                                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_BCC)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_BCC)
                                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_SUBJECT)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_SUBJECT)
                                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_BODY)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_BODY)
                                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .build();
 
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 62cf38b..7f5d202 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -17,6 +17,7 @@
 package android.app.appsearch;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -46,6 +47,7 @@
  */
 public final class AppSearchSchema {
     private static final String SCHEMA_TYPE_FIELD = "schemaType";
+    private static final String VERSION_FIELD = "version";
     private static final String PROPERTIES_FIELD = "properties";
 
     private final Bundle mBundle;
@@ -77,6 +79,11 @@
         return mBundle.getString(SCHEMA_TYPE_FIELD, "");
     }
 
+    /** Returns the version of this {@link AppSearchSchema}. */
+    public @IntRange(from = 0) int getVersion() {
+        return mBundle.getInt(VERSION_FIELD);
+    }
+
     /**
      * Returns the list of {@link PropertyConfig}s that are part of this schema.
      *
@@ -91,7 +98,7 @@
         }
         List<PropertyConfig> ret = new ArrayList<>(propertyBundles.size());
         for (int i = 0; i < propertyBundles.size(); i++) {
-            ret.add(new PropertyConfig(propertyBundles.get(i)));
+            ret.add(PropertyConfig.fromBundle(propertyBundles.get(i)));
         }
         return ret;
     }
@@ -108,12 +115,15 @@
         if (!getSchemaType().equals(otherSchema.getSchemaType())) {
             return false;
         }
+        if (getVersion() != otherSchema.getVersion()) {
+            return false;
+        }
         return getProperties().equals(otherSchema.getProperties());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(getSchemaType(), getProperties());
+        return Objects.hash(getSchemaType(), getVersion(), getProperties());
     }
 
     /** Builder for {@link AppSearchSchema objects}. */
@@ -121,6 +131,7 @@
         private final String mSchemaType;
         private final ArrayList<Bundle> mPropertyBundles = new ArrayList<>();
         private final Set<String> mPropertyNames = new ArraySet<>();
+        private int mVersion;
         private boolean mBuilt = false;
 
         /** Creates a new {@link AppSearchSchema.Builder}. */
@@ -147,6 +158,41 @@
         }
 
         /**
+         * Sets the version number of the {@link AppSearchSchema}.
+         *
+         * <p>The {@link AppSearchSession} database can only ever hold documents for one version of
+         * a {@link AppSearchSchema} type at a time.
+         *
+         * <p>Setting a version number that is different from the version number of the schema
+         * currently stored in AppSearch will result in AppSearch calling the Migrator provided to
+         * {@link AppSearchSession#setSchema} to migrate the documents already in AppSearch from the
+         * previous version to the one set in this request. The version number can be updated
+         * without any other changes to the schema.
+         *
+         * <p>The version number can stay the same, increase, or decrease relative to the current
+         * version number of the {@link AppSearchSchema} type that is already stored in the {@link
+         * AppSearchSession} database.
+         *
+         * <p>The version number will be updated if the {@link SetSchemaRequest} contains
+         * backwards-compatible changes or {@link SetSchemaRequest.Builder#setForceOverride} method
+         * is set to {@code true}.
+         *
+         * @param version A non-negative int number represents the version of this {@link
+         *     AppSearchSchema}, default version is 0.
+         * @throws IllegalStateException if the version is negative or the builder has already been
+         *     used.
+         * @see AppSearchSession#setSchema
+         */
+        // TODO(b/177266929) link to Migrator in once it's ready.
+        @NonNull
+        public AppSearchSchema.Builder setVersion(@IntRange(from = 0) int version) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentNonnegative(version);
+            mVersion = version;
+            return this;
+        }
+
+        /**
          * Constructs a new {@link AppSearchSchema} from the contents of this builder.
          *
          * <p>After calling this method, the builder must no longer be used.
@@ -156,6 +202,7 @@
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Bundle bundle = new Bundle();
             bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType);
+            bundle.putInt(AppSearchSchema.VERSION_FIELD, mVersion);
             bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles);
             mBuilt = true;
             return new AppSearchSchema(bundle);
@@ -163,18 +210,15 @@
     }
 
     /**
-     * Configuration for a single property (field) of a document type.
+     * Common configuration for a single property (field) in a Document.
      *
      * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be a
      * property.
      */
-    public static final class PropertyConfig {
-        private static final String NAME_FIELD = "name";
-        private static final String DATA_TYPE_FIELD = "dataType";
-        private static final String SCHEMA_TYPE_FIELD = "schemaType";
-        private static final String CARDINALITY_FIELD = "cardinality";
-        private static final String INDEXING_TYPE_FIELD = "indexingType";
-        private static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
+    public abstract static class PropertyConfig {
+        static final String NAME_FIELD = "name";
+        static final String DATA_TYPE_FIELD = "dataType";
+        static final String CARDINALITY_FIELD = "cardinality";
 
         /**
          * Physical data-types of the contents of the property.
@@ -195,18 +239,31 @@
         @Retention(RetentionPolicy.SOURCE)
         public @interface DataType {}
 
+        /** @hide */
         public static final int DATA_TYPE_STRING = 1;
+
+        /** @hide */
         public static final int DATA_TYPE_INT64 = 2;
+
+        /** @hide */
         public static final int DATA_TYPE_DOUBLE = 3;
+
+        /** @hide */
         public static final int DATA_TYPE_BOOLEAN = 4;
 
-        /** Unstructured BLOB. */
+        /**
+         * Unstructured BLOB.
+         *
+         * @hide
+         */
         public static final int DATA_TYPE_BYTES = 5;
 
         /**
          * Indicates that the property is itself a {@link GenericDocument}, making it part of a
          * hierarchical schema. Any property using this DataType MUST have a valid {@link
          * PropertyConfig#getSchemaType}.
+         *
+         * @hide
          */
         public static final int DATA_TYPE_DOCUMENT = 6;
 
@@ -235,6 +292,97 @@
         /** Exactly one value [1]. */
         public static final int CARDINALITY_REQUIRED = 3;
 
+        final Bundle mBundle;
+
+        @Nullable private Integer mHashCode;
+
+        PropertyConfig(@NonNull Bundle bundle) {
+            mBundle = Preconditions.checkNotNull(bundle);
+        }
+
+        @Override
+        public String toString() {
+            return mBundle.toString();
+        }
+
+        /** Returns the name of this property. */
+        @NonNull
+        public String getName() {
+            return mBundle.getString(NAME_FIELD, "");
+        }
+
+        /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
+        public @DataType int getDataType() {
+            return mBundle.getInt(DATA_TYPE_FIELD, -1);
+        }
+
+        /**
+         * Returns the cardinality of the property (whether it is optional, required or repeated).
+         */
+        public @Cardinality int getCardinality() {
+            return mBundle.getInt(CARDINALITY_FIELD, -1);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof PropertyConfig)) {
+                return false;
+            }
+            PropertyConfig otherProperty = (PropertyConfig) other;
+            return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle);
+        }
+
+        @Override
+        public int hashCode() {
+            if (mHashCode == null) {
+                mHashCode = BundleUtil.deepHashCode(mBundle);
+            }
+            return mHashCode;
+        }
+
+        /**
+         * Converts a {@link Bundle} into a {@link PropertyConfig} depending on its internal data
+         * type.
+         *
+         * <p>The bundle is not cloned.
+         *
+         * @throws IllegalArgumentException if the bundle does no contain a recognized value in its
+         *     {@code DATA_TYPE_FIELD}.
+         * @hide
+         */
+        @NonNull
+        public static PropertyConfig fromBundle(@NonNull Bundle propertyBundle) {
+            switch (propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)) {
+                case PropertyConfig.DATA_TYPE_STRING:
+                    return new StringPropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_INT64:
+                    return new Int64PropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_DOUBLE:
+                    return new DoublePropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_BOOLEAN:
+                    return new BooleanPropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_BYTES:
+                    return new BytesPropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_DOCUMENT:
+                    return new DocumentPropertyConfig(propertyBundle);
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported property bundle of type "
+                                    + propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)
+                                    + "; contents: "
+                                    + propertyBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property of type String in a Document. */
+    public static final class StringPropertyConfig extends PropertyConfig {
+        private static final String INDEXING_TYPE_FIELD = "indexingType";
+        private static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
+
         /**
          * Encapsulates the configurations on how AppSearch should query/index these terms.
          *
@@ -249,14 +397,7 @@
         @Retention(RetentionPolicy.SOURCE)
         public @interface IndexingType {}
 
-        /**
-         * Content in this property will not be tokenized or indexed.
-         *
-         * <p>Useful if the data type is not made up of terms (e.g. {@link
-         * PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} type). None
-         * of the properties inside the nested property will be indexed regardless of the value of
-         * {@code indexingType} for the nested properties.
-         */
+        /** Content in this property will not be tokenized or indexed. */
         public static final int INDEXING_TYPE_NONE = 0;
 
         /**
@@ -291,54 +432,16 @@
         public @interface TokenizerType {}
 
         /**
-         * It is only valid for tokenizer_type to be 'NONE' if the data type is {@link
-         * PropertyConfig#DATA_TYPE_DOCUMENT}.
+         * It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is {@link
+         * #INDEXING_TYPE_NONE}.
          */
         public static final int TOKENIZER_TYPE_NONE = 0;
 
         /** Tokenization for plain text. */
         public static final int TOKENIZER_TYPE_PLAIN = 1;
 
-        final Bundle mBundle;
-
-        @Nullable private Integer mHashCode;
-
-        PropertyConfig(@NonNull Bundle bundle) {
-            mBundle = Preconditions.checkNotNull(bundle);
-        }
-
-        @Override
-        public String toString() {
-            return mBundle.toString();
-        }
-
-        /** Returns the name of this property. */
-        @NonNull
-        public String getName() {
-            return mBundle.getString(NAME_FIELD, "");
-        }
-
-        /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
-        public @DataType int getDataType() {
-            return mBundle.getInt(DATA_TYPE_FIELD, -1);
-        }
-
-        /**
-         * Returns the logical schema-type of the contents of this property.
-         *
-         * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. Otherwise,
-         * it is {@code null}.
-         */
-        @Nullable
-        public String getSchemaType() {
-            return mBundle.getString(SCHEMA_TYPE_FIELD);
-        }
-
-        /**
-         * Returns the cardinality of the property (whether it is optional, required or repeated).
-         */
-        public @Cardinality int getCardinality() {
-            return mBundle.getInt(CARDINALITY_FIELD, -1);
+        StringPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
         }
 
         /** Returns how the property is indexed. */
@@ -351,75 +454,19 @@
             return mBundle.getInt(TOKENIZER_TYPE_FIELD);
         }
 
-        @Override
-        public boolean equals(@Nullable Object other) {
-            if (this == other) {
-                return true;
-            }
-            if (!(other instanceof PropertyConfig)) {
-                return false;
-            }
-            PropertyConfig otherProperty = (PropertyConfig) other;
-            return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle);
-        }
-
-        @Override
-        public int hashCode() {
-            if (mHashCode == null) {
-                mHashCode = BundleUtil.deepHashCode(mBundle);
-            }
-            return mHashCode;
-        }
-
         /**
-         * Builder for {@link PropertyConfig}.
+         * Builder for {@link StringPropertyConfig}.
          *
-         * <p>The following properties must be set, or {@link PropertyConfig} construction will
-         * fail:
-         *
-         * <ul>
-         *   <li>dataType
-         *   <li>cardinality
-         * </ul>
-         *
-         * <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
-         * is also required.
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
          */
         public static final class Builder {
             private final Bundle mBundle = new Bundle();
             private boolean mBuilt = false;
 
-            /** Creates a new {@link PropertyConfig.Builder}. */
+            /** Creates a new {@link StringPropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
                 mBundle.putString(NAME_FIELD, propertyName);
-            }
-
-            /**
-             * Type of data the property contains (e.g. string, int, bytes, etc).
-             *
-             * <p>This property must be set.
-             */
-            @NonNull
-            public PropertyConfig.Builder setDataType(@DataType int dataType) {
-                Preconditions.checkState(!mBuilt, "Builder has already been used");
-                Preconditions.checkArgumentInRange(
-                        dataType, DATA_TYPE_STRING, DATA_TYPE_DOCUMENT, "dataType");
-                mBundle.putInt(DATA_TYPE_FIELD, dataType);
-                return this;
-            }
-
-            /**
-             * The logical schema-type of the contents of this property.
-             *
-             * <p>Only required when {@link #setDataType} is set to {@link #DATA_TYPE_DOCUMENT}.
-             * Otherwise, it is ignored.
-             */
-            @NonNull
-            public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
-                Preconditions.checkState(!mBuilt, "Builder has already been used");
-                Preconditions.checkNotNull(schemaType);
-                mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
-                return this;
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_STRING);
             }
 
             /**
@@ -427,8 +474,9 @@
              *
              * <p>This property must be set.
              */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
             @NonNull
-            public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+            public StringPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Preconditions.checkArgumentInRange(
                         cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
@@ -440,7 +488,7 @@
              * Configures how a property should be indexed so that it can be retrieved by queries.
              */
             @NonNull
-            public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
+            public StringPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Preconditions.checkArgumentInRange(
                         indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType");
@@ -450,7 +498,7 @@
 
             /** Configures how this property should be tokenized (split into words). */
             @NonNull
-            public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
+            public StringPropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Preconditions.checkArgumentInRange(
                         tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType");
@@ -459,6 +507,337 @@
             }
 
             /**
+             * Constructs a new {@link StringPropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public StringPropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead
+                //     of partially reimplementing some of the validation Icing does here.
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new StringPropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a 64-bit integer. */
+    public static final class Int64PropertyConfig extends PropertyConfig {
+        Int64PropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link Int64PropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link Int64PropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_INT64);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public Int64PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link Int64PropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public Int64PropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new Int64PropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a double-precision decimal number. */
+    public static final class DoublePropertyConfig extends PropertyConfig {
+        DoublePropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link DoublePropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link DoublePropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOUBLE);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public DoublePropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link DoublePropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public DoublePropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new DoublePropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a boolean. */
+    public static final class BooleanPropertyConfig extends PropertyConfig {
+        BooleanPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link BooleanPropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link BooleanPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BOOLEAN);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public BooleanPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link BooleanPropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public BooleanPropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new BooleanPropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a byte array. */
+    public static final class BytesPropertyConfig extends PropertyConfig {
+        BytesPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link BytesPropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link BytesPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BYTES);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public BytesPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link BytesPropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public BytesPropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new BytesPropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing another Document. */
+    public static final class DocumentPropertyConfig extends PropertyConfig {
+        private static final String SCHEMA_TYPE_FIELD = "schemaType";
+        private static final String INDEX_NESTED_PROPERTIES_FIELD = "indexNestedProperties";
+
+        DocumentPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /** Returns the logical schema-type of the contents of this document property. */
+        @NonNull
+        public String getSchemaType() {
+            return Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+        }
+
+        /**
+         * Returns whether fields in the nested document should be indexed according to that
+         * document's schema.
+         *
+         * <p>If false, the nested document's properties are not indexed regardless of its own
+         * schema.
+         */
+        public boolean isIndexNestedProperties() {
+            return mBundle.getBoolean(INDEX_NESTED_PROPERTIES_FIELD);
+        }
+
+        /**
+         * Builder for {@link DocumentPropertyConfig}.
+         *
+         * <p>The following properties must be set, or {@link DocumentPropertyConfig} construction
+         * will fail:
+         *
+         * <ul>
+         *   <li>cardinality
+         *   <li>schemaType
+         * </ul>
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link DocumentPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT);
+            }
+
+            /**
+             * The logical schema-type of the contents of this property.
+             *
+             * <p>This property must be set.
+             */
+            @NonNull
+            public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkNotNull(schemaType);
+                mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
+                return this;
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public DocumentPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Configures whether fields in the nested document should be indexed according to that
+             * document's schema.
+             *
+             * <p>If false, the nested document's properties are not indexed regardless of its own
+             * schema.
+             */
+            @NonNull
+            public DocumentPropertyConfig.Builder setIndexNestedProperties(
+                    boolean indexNestedProperties) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                mBundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, indexNestedProperties);
+                return this;
+            }
+
+            /**
              * Constructs a new {@link PropertyConfig} from the contents of this builder.
              *
              * <p>After calling this method, the builder must no longer be used.
@@ -467,24 +846,16 @@
              *     missing {@code dataType}).
              */
             @NonNull
-            public PropertyConfig build() {
+            public DocumentPropertyConfig build() {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
-                // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead
-                //     of partially reimplementing some of the validation Icing does here.
-                if (!mBundle.containsKey(DATA_TYPE_FIELD)) {
-                    throw new IllegalSchemaException("Missing field: dataType");
-                }
-                if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()
-                        && mBundle.getInt(DATA_TYPE_FIELD) == DATA_TYPE_DOCUMENT) {
-                    throw new IllegalSchemaException(
-                            "Missing field: schemaType (required for configs with "
-                                    + "dataType = DOCUMENT)");
+                if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()) {
+                    throw new IllegalSchemaException("Missing field: schemaType");
                 }
                 if (!mBundle.containsKey(CARDINALITY_FIELD)) {
                     throw new IllegalSchemaException("Missing field: cardinality");
                 }
                 mBuilt = true;
-                return new PropertyConfig(mBundle);
+                return new DocumentPropertyConfig(mBundle);
             }
         }
     }
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 11e7fab..2f02808 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -80,9 +80,9 @@
     /**
      * The maximum number of indexed properties a document can have.
      *
-     * <p>Indexed properties are properties where the {@link
-     * AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than {@link
-     * AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
+     * <p>Indexed properties are properties which are strings where the {@link
+     * AppSearchSchema.StringPropertyConfig#getIndexingType} value is anything other than {@link
+     * AppSearchSchema.StringPropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
      */
     public static int getMaxIndexedProperties() {
         return MAX_INDEXED_PROPERTIES;
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 74afdd2..38e0046 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -17,13 +17,17 @@
 package android.app.appsearch;
 
 import android.annotation.NonNull;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -32,12 +36,24 @@
  * @see AppSearchSession#getByUri
  */
 public final class GetByUriRequest {
+    /**
+     * Schema type to be used in {@link android.app.appsearch.GetByUriRequest.Builder#addProjection}
+     * to apply property paths to all results, excepting any types that have had their own, specific
+     * property paths set.
+     */
+    public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+
     private final String mNamespace;
     private final Set<String> mUris;
+    private final Map<String, List<String>> mTypePropertyPathsMap;
 
-    GetByUriRequest(@NonNull String namespace, @NonNull Set<String> uris) {
-        mNamespace = namespace;
-        mUris = uris;
+    GetByUriRequest(
+            @NonNull String namespace,
+            @NonNull Set<String> uris,
+            @NonNull Map<String, List<String>> typePropertyPathsMap) {
+        mNamespace = Preconditions.checkNotNull(namespace);
+        mUris = Preconditions.checkNotNull(uris);
+        mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap);
     }
 
     /** Returns the namespace to get documents from. */
@@ -52,10 +68,43 @@
         return Collections.unmodifiableSet(mUris);
     }
 
+    /**
+     * Returns a map from schema type to property paths to be used for projection.
+     *
+     * <p>If the map is empty, then all properties will be retrieved for all results.
+     *
+     * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
+     * function, rather than calling it multiple times.
+     */
+    @NonNull
+    public Map<String, List<String>> getProjections() {
+        Map<String, List<String>> copy = new ArrayMap<>();
+        for (String key : mTypePropertyPathsMap.keySet()) {
+            copy.put(key, new ArrayList<>(mTypePropertyPathsMap.get(key)));
+        }
+        return copy;
+    }
+
+    /**
+     * Returns a map from schema type to property paths to be used for projection.
+     *
+     * <p>If the map is empty, then all properties will be retrieved for all results.
+     *
+     * <p>A more efficient version of {@link #getProjections}, but it returns a modifiable map. This
+     * is not meant to be unhidden and should only be used by internal classes.
+     *
+     * @hide
+     */
+    @NonNull
+    public Map<String, List<String>> getProjectionsVisibleToPackagesInternal() {
+        return mTypePropertyPathsMap;
+    }
+
     /** Builder for {@link GetByUriRequest} objects. */
     public static final class Builder {
         private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
         private final Set<String> mUris = new ArraySet<>();
+        private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
         private boolean mBuilt = false;
 
         /**
@@ -87,12 +136,63 @@
             return this;
         }
 
+        /**
+         * Adds property paths for the specified type to be used for projection. If property paths
+         * are added for a type, then only the properties referred to will be retrieved for results
+         * of that type. If a property path that is specified isn't present in a result, it will be
+         * ignored for that result. Property paths cannot be null.
+         *
+         * <p>If no property paths are added for a particular type, then all properties of results
+         * of that type will be retrieved.
+         *
+         * <p>If property path is added for the {@link
+         * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
+         * all results, excepting any types that have their own, specific property paths set.
+         *
+         * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+         */
+        @NonNull
+        public Builder addProjection(@NonNull String schemaType, @NonNull String... propertyPaths) {
+            Preconditions.checkNotNull(propertyPaths);
+            return addProjection(schemaType, Arrays.asList(propertyPaths));
+        }
+
+        /**
+         * Adds property paths for the specified type to be used for projection. If property paths
+         * are added for a type, then only the properties referred to will be retrieved for results
+         * of that type. If a property path that is specified isn't present in a result, it will be
+         * ignored for that result. Property paths cannot be null.
+         *
+         * <p>If no property paths are added for a particular type, then all properties of results
+         * of that type will be retrieved.
+         *
+         * <p>If property path is added for the {@link
+         * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
+         * all results, excepting any types that have their own, specific property paths set.
+         *
+         * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+         */
+        @NonNull
+        public Builder addProjection(
+                @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(propertyPaths);
+            List<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
+            for (String propertyPath : propertyPaths) {
+                Preconditions.checkNotNull(propertyPath);
+                propertyPathsList.add(propertyPath);
+            }
+            mProjectionTypePropertyPaths.put(schemaType, propertyPathsList);
+            return this;
+        }
+
         /** Builds a new {@link GetByUriRequest}. */
         @NonNull
         public GetByUriRequest build() {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mBuilt = true;
-            return new GetByUriRequest(mNamespace, mUris);
+            return new GetByUriRequest(mNamespace, mUris, mProjectionTypePropertyPaths);
         }
     }
 }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
new file mode 100644
index 0000000..2bfcf28
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A request to report usage of a document.
+ *
+ * <p>See {@link AppSearchSession#reportUsage} for a detailed description of usage reporting.
+ *
+ * @see AppSearchSession#reportUsage
+ */
+public final class ReportUsageRequest {
+    private final String mNamespace;
+    private final String mUri;
+    private final long mUsageTimeMillis;
+
+    ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) {
+        mNamespace = Preconditions.checkNotNull(namespace);
+        mUri = Preconditions.checkNotNull(uri);
+        mUsageTimeMillis = usageTimeMillis;
+    }
+
+    /** Returns the namespace of the document that was used. */
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** Returns the URI of document that was used. */
+    @NonNull
+    public String getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns the timestamp in milliseconds of the usage report (the time at which the document was
+     * used).
+     *
+     * <p>The value is in the {@link System#currentTimeMillis} time base.
+     */
+    public long getUsageTimeMillis() {
+        return mUsageTimeMillis;
+    }
+
+    /** Builder for {@link ReportUsageRequest} objects. */
+    public static final class Builder {
+        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private String mUri;
+        private Long mUsageTimeMillis;
+        private boolean mBuilt = false;
+
+        /**
+         * Sets which namespace the document being used belongs to.
+         *
+         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         *
+         * @throws IllegalStateException if the builder has already been used
+         */
+        @NonNull
+        public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(namespace);
+            mNamespace = namespace;
+            return this;
+        }
+
+        /**
+         * Sets the URI of the document being used.
+         *
+         * <p>This field is required.
+         *
+         * @throws IllegalStateException if the builder has already been used
+         */
+        @NonNull
+        public ReportUsageRequest.Builder setUri(@NonNull String uri) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(uri);
+            mUri = uri;
+            return this;
+        }
+
+        /**
+         * Sets the timestamp in milliseconds of the usage report (the time at which the document
+         * was used).
+         *
+         * <p>The value is in the {@link System#currentTimeMillis} time base.
+         *
+         * <p>If unset, this defaults to the current timestamp at the time that the {@link
+         * ReportUsageRequest} is constructed.
+         *
+         * @throws IllegalStateException if the builder has already been used
+         */
+        @NonNull
+        public ReportUsageRequest.Builder setUsageTimeMillis(long usageTimeMillis) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mUsageTimeMillis = usageTimeMillis;
+            return this;
+        }
+
+        /**
+         * Builds a new {@link ReportUsageRequest}.
+         *
+         * @throws NullPointerException if {@link #setUri} has never been called
+         * @throws IllegalStateException if the builder has already been used
+         */
+        @NonNull
+        public ReportUsageRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI");
+            if (mUsageTimeMillis == null) {
+                mUsageTimeMillis = System.currentTimeMillis();
+            }
+            mBuilt = true;
+            return new ReportUsageRequest(mNamespace, mUri, mUsageTimeMillis);
+        }
+    }
+}
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 62324b2..d792f45 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -125,7 +125,6 @@
      * Contains the database name that stored the {@link GenericDocument}.
      *
      * @return Database name that stored the document
-     * @hide
      */
     @NonNull
     public String getDatabaseName() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index b5d5f04d..963062c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -106,7 +106,9 @@
                 RANKING_STRATEGY_NONE,
                 RANKING_STRATEGY_DOCUMENT_SCORE,
                 RANKING_STRATEGY_CREATION_TIMESTAMP,
-                RANKING_STRATEGY_RELEVANCE_SCORE
+                RANKING_STRATEGY_RELEVANCE_SCORE,
+                RANKING_STRATEGY_USAGE_COUNT,
+                RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface RankingStrategy {}
@@ -119,6 +121,10 @@
     public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2;
     /** Ranked by document relevance score. */
     public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3;
+    /** Ranked by number of usages. */
+    public static final int RANKING_STRATEGY_USAGE_COUNT = 4;
+    /** Ranked by timestamp of last usage. */
+    public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5;
 
     /**
      * Order for query result.
@@ -411,7 +417,7 @@
             Preconditions.checkArgumentInRange(
                     rankingStrategy,
                     RANKING_STRATEGY_NONE,
-                    RANKING_STRATEGY_RELEVANCE_SCORE,
+                    RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP,
                     "Result ranking strategy");
             mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
             return this;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 1f1e9a1..ec41353 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -31,26 +31,29 @@
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
 import android.content.Context;
+import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelableException;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
-/**
- * TODO(b/142567528): add comments when implement this class
- */
+/** TODO(b/142567528): add comments when implement this class */
 public class AppSearchManagerService extends SystemService {
     private static final String TAG = "AppSearchManagerService";
+    private PackageManagerInternal mPackageManagerInternal;
 
     public AppSearchManagerService(Context context) {
         super(context);
@@ -59,11 +62,13 @@
     @Override
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
     }
 
     private class Stub extends IAppSearchManager.Stub {
         @Override
         public void setSchema(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull List<Bundle> schemaBundles,
                 @NonNull List<String> schemasNotPlatformSurfaceable,
@@ -71,6 +76,7 @@
                 boolean forceOverride,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(schemaBundles);
             Preconditions.checkNotNull(callback);
@@ -78,6 +84,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
                 for (int i = 0; i < schemaBundles.size(); i++) {
                     schemas.add(new AppSearchSchema(schemaBundles.get(i)));
@@ -94,7 +101,6 @@
                     schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
                 }
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
                 impl.setSchema(
                         packageName,
                         databaseName,
@@ -102,8 +108,8 @@
                         schemasNotPlatformSurfaceable,
                         schemasPackageAccessible,
                         forceOverride);
-                invokeCallbackOnResult(callback,
-                        AppSearchResult.newSuccessfulResult(/*result=*/ null));
+                invokeCallbackOnResult(
+                        callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
             } finally {
@@ -113,24 +119,26 @@
 
         @Override
         public void getSchema(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
                 List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
                 List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
                 for (int i = 0; i < schemas.size(); i++) {
                     schemaBundles.add(schemas.get(i).getBundle());
                 }
-                invokeCallbackOnResult(callback,
-                        AppSearchResult.newSuccessfulResult(schemaBundles));
+                invokeCallbackOnResult(
+                        callback, AppSearchResult.newSuccessfulResult(schemaBundles));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
             } finally {
@@ -140,10 +148,12 @@
 
         @Override
         public void putDocuments(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull List<Bundle> documentBundles,
                 @UserIdInt int userId,
                 @NonNull IAppSearchBatchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(documentBundles);
             Preconditions.checkNotNull(callback);
@@ -151,10 +161,10 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
                 for (int i = 0; i < documentBundles.size(); i++) {
                     GenericDocument document = new GenericDocument(documentBundles.get(i));
                     try {
@@ -176,11 +186,14 @@
 
         @Override
         public void getDocuments(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull String namespace,
                 @NonNull List<String> uris,
+                @NonNull Map<String, List<String>> typePropertyPaths,
                 @UserIdInt int userId,
                 @NonNull IAppSearchBatchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(namespace);
             Preconditions.checkNotNull(uris);
@@ -189,15 +202,15 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
                         GenericDocument document = impl.getDocument(packageName, databaseName,
-                                namespace, uri);
+                                namespace, uri, typePropertyPaths);
                         resultBuilder.setSuccess(uri, document.getBundle());
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -214,11 +227,13 @@
         // TODO(sidchhabra): Do this in a threadpool.
         @Override
         public void query(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(queryExpression);
             Preconditions.checkNotNull(searchSpecBundle);
@@ -227,14 +242,16 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
-                SearchResultPage searchResultPage = impl.query(
-                        packageName,
-                        databaseName,
-                        queryExpression,
-                        new SearchSpec(searchSpecBundle));
-                invokeCallbackOnResult(callback,
+                SearchResultPage searchResultPage =
+                        impl.query(
+                                packageName,
+                                databaseName,
+                                queryExpression,
+                                new SearchSpec(searchSpecBundle));
+                invokeCallbackOnResult(
+                        callback,
                         AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
@@ -245,10 +262,12 @@
 
         @Override
         public void globalQuery(
+                @NonNull String packageName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(queryExpression);
             Preconditions.checkNotNull(searchSpecBundle);
             Preconditions.checkNotNull(callback);
@@ -256,11 +275,15 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                 SearchResultPage searchResultPage = impl.globalQuery(
                         queryExpression,
-                        new SearchSpec(searchSpecBundle));
-                invokeCallbackOnResult(callback,
+                        new SearchSpec(searchSpecBundle),
+                        packageName,
+                        callingUid);
+                invokeCallbackOnResult(
+                        callback,
                         AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
@@ -270,7 +293,9 @@
         }
 
         @Override
-        public void getNextPage(long nextPageToken, @UserIdInt int userId,
+        public void getNextPage(
+                long nextPageToken,
+                @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
@@ -281,7 +306,8 @@
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                 SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
-                invokeCallbackOnResult(callback,
+                invokeCallbackOnResult(
+                        callback,
                         AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
@@ -306,12 +332,47 @@
         }
 
         @Override
+        public void reportUsage(
+                @NonNull String packageName,
+                @NonNull String databaseName,
+                @NonNull String namespace,
+                @NonNull String uri,
+                long usageTimeMillis,
+                @UserIdInt int userId,
+                @NonNull IAppSearchResultCallback callback) {
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(namespace);
+            Objects.requireNonNull(uri);
+            Objects.requireNonNull(callback);
+            int callingUid = Binder.getCallingUid();
+            int callingUserId = handleIncomingUser(userId, callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                impl.reportUsage(
+                        packageName,
+                        databaseName,
+                        namespace,
+                        uri,
+                        usageTimeMillis);
+                invokeCallbackOnResult(callback,
+                        AppSearchResult.newSuccessfulResult(/*result=*/ null));
+            } catch (Throwable t) {
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
         public void removeByUri(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull String namespace,
                 @NonNull List<String> uris,
                 @UserIdInt int userId,
                 @NonNull IAppSearchBatchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(uris);
             Preconditions.checkNotNull(callback);
@@ -319,15 +380,15 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
                         impl.remove(packageName, databaseName, namespace, uri);
-                        resultBuilder.setSuccess(uri, /*result= */null);
+                        resultBuilder.setSuccess(uri, /*result= */ null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
                     }
@@ -342,11 +403,13 @@
 
         @Override
         public void removeByQuery(
+                @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(queryExpression);
             Preconditions.checkNotNull(searchSpecBundle);
@@ -355,9 +418,12 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String packageName = convertUidToPackageName(callingUid);
-                impl.removeByQuery(packageName, databaseName, queryExpression,
+                impl.removeByQuery(
+                        packageName,
+                        databaseName,
+                        queryExpression,
                         new SearchSpec(searchSpecBundle));
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
@@ -398,28 +464,22 @@
             }
         }
 
-        /**
-         * Returns a package name for the given uid.
-         *
-         * <p>The current implementation returns the package name of the app with this uid in a
-         * format like {@code com.example.package} or {@code com.example.sharedname:5678}.
-         */
-        @NonNull
-        private String convertUidToPackageName(int callingUid) {
-            // For regular apps, this call will return the package name. If callingUid is an
-            // android:sharedUserId, this value may be another type of name and have a :uid suffix.
-            String callingUidName = getContext().getPackageManager().getNameForUid(callingUid);
-            if (callingUidName == null) {
-                // Not sure how this is possible --- maybe app was uninstalled?
-                throw new IllegalStateException(
-                        "Failed to look up package name for uid " + callingUid);
+        private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) {
+            Preconditions.checkNotNull(callingPackage);
+            if (mPackageManagerInternal.getPackageUid(
+                            callingPackage, /*flags=*/ 0, UserHandle.getUserId(callingUid))
+                    != callingUid) {
+                throw new SecurityException(
+                        "Specified calling package ["
+                                + callingPackage
+                                + "] does not match the calling uid "
+                                + callingUid);
             }
-            return callingUidName;
         }
 
         /** Invokes the {@link IAppSearchResultCallback} with the result. */
-        private void invokeCallbackOnResult(IAppSearchResultCallback callback,
-                AppSearchResult<?> result) {
+        private void invokeCallbackOnResult(
+                IAppSearchResultCallback callback, AppSearchResult<?> result) {
             try {
                 callback.onResult(result);
             } catch (RemoteException e) {
@@ -428,8 +488,8 @@
         }
 
         /** Invokes the {@link IAppSearchBatchResultCallback} with the result. */
-        private void invokeCallbackOnResult(IAppSearchBatchResultCallback callback,
-                AppSearchBatchResult<?, ?> result) {
+        private void invokeCallbackOnResult(
+                IAppSearchBatchResultCallback callback, AppSearchBatchResult<?, ?> result) {
             try {
                 callback.onResult(result);
             } catch (RemoteException e) {
@@ -455,8 +515,8 @@
          *
          * <p>The throwable is converted to {@link ParcelableException}.
          */
-        private void invokeCallbackOnError(IAppSearchBatchResultCallback callback,
-                Throwable throwable) {
+        private void invokeCallbackOnError(
+                IAppSearchBatchResultCallback callback, Throwable throwable) {
             try {
                 callback.onSystemError(new ParcelableException(throwable));
             } catch (RemoteException e) {
@@ -465,13 +525,18 @@
         }
     }
 
-    //TODO(b/173553485) verifying that the caller has permission to access target user's data
-    //TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast
-    //TODO(b/173553485) Implement SystemService.onUserStopping()
+    // TODO(b/173553485) verifying that the caller has permission to access target user's data
+    // TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast
+    // TODO(b/173553485) Implement SystemService.onUserStopping()
     private static int handleIncomingUser(@UserIdInt int userId, int callingUid) {
         int callingPid = Binder.getCallingPid();
-        return ActivityManager.handleIncomingUser(callingPid, callingUid, userId,
-                /*allowAll=*/ false, /*requireFull=*/ false,
-                /*name=*/ null, /*callerPackage=*/ null);
+        return ActivityManager.handleIncomingUser(
+                callingPid,
+                callingUid,
+                userId,
+                /*allowAll=*/ false,
+                /*requireFull=*/ false,
+                /*name=*/ null,
+                /*callerPackage=*/ null);
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 2871eb6..cd90f85 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -69,7 +69,7 @@
     private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
         File appSearchDir = getAppSearchDir(context, userId);
-        return AppSearchImpl.create(appSearchDir);
+        return AppSearchImpl.create(appSearchDir, context, /*globalQuerierPackage=*/"");
     }
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 674f199..1665b1c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -21,10 +21,12 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
+import android.app.appsearch.GetByUriRequest;
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
+import android.content.Context;
 import android.os.Bundle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -37,6 +39,7 @@
 import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
+import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
 
 import com.google.android.icing.IcingSearchEngine;
 import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -54,6 +57,7 @@
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.PropertyProto;
 import com.google.android.icing.proto.PutResultProto;
+import com.google.android.icing.proto.ReportUsageResultProto;
 import com.google.android.icing.proto.ResetResultProto;
 import com.google.android.icing.proto.ResultSpecProto;
 import com.google.android.icing.proto.SchemaProto;
@@ -64,6 +68,7 @@
 import com.google.android.icing.proto.SetSchemaResultProto;
 import com.google.android.icing.proto.StatusProto;
 import com.google.android.icing.proto.TypePropertyMask;
+import com.google.android.icing.proto.UsageReport;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -154,14 +159,20 @@
      * folder.
      */
     @NonNull
-    public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
+    public static AppSearchImpl create(
+            @NonNull File icingDir, @NonNull Context context, @NonNull String globalQuerierPackage)
+            throws AppSearchException {
         Preconditions.checkNotNull(icingDir);
-        AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir);
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(globalQuerierPackage);
+        AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir, context, globalQuerierPackage);
         appSearchImpl.initializeVisibilityStore();
         return appSearchImpl;
     }
 
-    private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
+    private AppSearchImpl(
+            @NonNull File icingDir, @NonNull Context context, @NonNull String globalQuerierPackage)
+            throws AppSearchException {
         mReadWriteLock.writeLock().lock();
 
         try {
@@ -173,7 +184,7 @@
                             .build();
             mIcingSearchEngineLocked = new IcingSearchEngine(options);
 
-            mVisibilityStoreLocked = new VisibilityStore(this);
+            mVisibilityStoreLocked = new VisibilityStore(this, context, globalQuerierPackage);
 
             InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
             SchemaProto schemaProto;
@@ -419,6 +430,8 @@
      * @param databaseName The databaseName this document resides in.
      * @param namespace The namespace this document resides in.
      * @param uri The URI of the document to get.
+     * @param typePropertyPaths A map of schema type to a list of property paths to return in the
+     *     result.
      * @return The Document contents
      * @throws AppSearchException on IcingSearchEngine error.
      */
@@ -427,16 +440,35 @@
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull String namespace,
-            @NonNull String uri)
+            @NonNull String uri,
+            @NonNull Map<String, List<String>> typePropertyPaths)
             throws AppSearchException {
         GetResultProto getResultProto;
+        List<TypePropertyMask> nonPrefixedPropertyMasks =
+                TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
+        List<TypePropertyMask> prefixedPropertyMasks =
+                new ArrayList<>(nonPrefixedPropertyMasks.size());
+        for (int i = 0; i < nonPrefixedPropertyMasks.size(); ++i) {
+            TypePropertyMask typePropertyMask = nonPrefixedPropertyMasks.get(i);
+            String nonPrefixedType = typePropertyMask.getSchemaType();
+            String prefixedType =
+                    nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD)
+                            ? nonPrefixedType
+                            : createPrefix(packageName, databaseName) + nonPrefixedType;
+            prefixedPropertyMasks.add(
+                    typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
+        }
+        GetResultSpecProto getResultSpec =
+                GetResultSpecProto.newBuilder()
+                        .addAllTypePropertyMasks(prefixedPropertyMasks)
+                        .build();
         mReadWriteLock.readLock().lock();
         try {
             getResultProto =
                     mIcingSearchEngineLocked.get(
                             createPrefix(packageName, databaseName) + namespace,
                             uri,
-                            GetResultSpecProto.getDefaultInstance());
+                            getResultSpec);
         } finally {
             mReadWriteLock.readLock().unlock();
         }
@@ -476,8 +508,12 @@
 
         mReadWriteLock.readLock().lock();
         try {
+            String prefix = createPrefix(packageName, databaseName);
+            Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemas(prefix, searchSpec);
+
             return doQueryLocked(
                     Collections.singleton(createPrefix(packageName, databaseName)),
+                    allowedPrefixedSchemas,
                     queryExpression,
                     searchSpec);
         } finally {
@@ -493,40 +529,72 @@
      *
      * @param queryExpression Query String to search.
      * @param searchSpec Spec for setting filters, raw query etc.
+     * @param callerPackageName Package name of the caller, should belong to the {@code callerUid}.
+     * @param callerUid UID of the client making the globalQuery call.
      * @return The results of performing this search. It may contain an empty list of results if no
      *     documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
     public SearchResultPage globalQuery(
-            @NonNull String queryExpression, @NonNull SearchSpec searchSpec)
+            @NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull String callerPackageName,
+            int callerUid)
             throws AppSearchException {
-        // TODO(b/169883602): Check if the platform is querying us at a higher level. At this
-        //  point, we should add all platform-surfaceable schemas assuming the querier has been
-        //  verified.
         mReadWriteLock.readLock().lock();
         try {
-            Set<String> prefixes = new ArraySet<>();
             Set<String> packageFilters = new ArraySet<>(searchSpec.getPackageNames());
-
-            for (String prefix : mNamespaceMapLocked.keySet()) {
-                if (prefix.equals(VisibilityStore.VISIBILITY_STORE_PREFIX)) {
-                    // Filter out any VisibilityStore documents which are AppSearch-internal only.
-                    continue;
+            Set<String> prefixFilters = new ArraySet<>();
+            Set<String> allPrefixes = mNamespaceMapLocked.keySet();
+            if (packageFilters.isEmpty()) {
+                // Client didn't restrict their search over packages. Try to query over all
+                // packages/prefixes
+                prefixFilters = allPrefixes;
+            } else {
+                // Client did restrict their search over packages. Only include the prefixes that
+                // belong to the specified packages.
+                for (String prefix : allPrefixes) {
+                    String packageName = getPackageName(prefix);
+                    if (packageFilters.contains(packageName)) {
+                        prefixFilters.add(prefix);
+                    }
                 }
-
-                if (!packageFilters.isEmpty() && !packageFilters.contains(getPackageName(prefix))) {
-                    // Client wanted to restrict search over specified packages. Since the
-                    // specified packages don't include this prefix, don't add it to our search
-                    // filters.
-                    continue;
-                }
-
-                // Otherwise, include this prefix in our global search.
-                prefixes.add(prefix);
             }
 
-            return doQueryLocked(prefixes, queryExpression, searchSpec);
+            // Find which schemas the client is allowed to query over.
+            Set<String> allowedPrefixedSchemas = new ArraySet<>();
+            List<String> schemaFilters = searchSpec.getSchemaTypes();
+            for (String prefix : prefixFilters) {
+                String packageName = getPackageName(prefix);
+
+                if (!schemaFilters.isEmpty()) {
+                    for (String schema : schemaFilters) {
+                        // Client specified some schemas to search over, check each one
+                        String prefixedSchema = prefix + schema;
+                        if (packageName.equals(callerPackageName)
+                                || mVisibilityStoreLocked.isSchemaSearchableByCaller(
+                                        prefix, prefixedSchema, callerUid)) {
+                            allowedPrefixedSchemas.add(prefixedSchema);
+                        }
+                    }
+                } else {
+                    // Client didn't specify certain schemas to search over, check all schemas
+                    Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix);
+                    if (prefixedSchemas != null) {
+                        for (String prefixedSchema : prefixedSchemas) {
+                            if (packageName.equals(callerPackageName)
+                                    || mVisibilityStoreLocked.isSchemaSearchableByCaller(
+                                            prefix, prefixedSchema, callerUid)) {
+                                allowedPrefixedSchemas.add(prefixedSchema);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return doQueryLocked(
+                    prefixFilters, allowedPrefixedSchemas, queryExpression, searchSpec);
         } finally {
             mReadWriteLock.readLock().unlock();
         }
@@ -535,28 +603,25 @@
     @GuardedBy("mReadWriteLock")
     private SearchResultPage doQueryLocked(
             @NonNull Set<String> prefixes,
+            @NonNull Set<String> allowedPrefixedSchemas,
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec)
             throws AppSearchException {
         SearchSpecProto.Builder searchSpecBuilder =
                 SearchSpecToProtoConverter.toSearchSpecProto(searchSpec).toBuilder()
                         .setQuery(queryExpression);
-        // rewriteSearchSpecForPrefixesLocked will return false if none of the prefixes that the
-        // client is trying to search on exist, so we can return an empty SearchResult and skip
+        // rewriteSearchSpecForPrefixesLocked will return false if there is nothing to search
+        // over given their search filters, so we can return an empty SearchResult and skip
         // sending request to Icing.
-        if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder, prefixes)) {
+        if (!rewriteSearchSpecForPrefixesLocked(
+                searchSpecBuilder, prefixes, allowedPrefixedSchemas)) {
             return new SearchResultPage(Bundle.EMPTY);
         }
 
         ResultSpecProto.Builder resultSpecBuilder =
                 SearchSpecToProtoConverter.toResultSpecProto(searchSpec).toBuilder();
 
-        // rewriteResultSpecForPrefixesLocked will return false if none of the prefixes that the
-        // client is trying to search on exist, so we can return an empty SearchResult and skip
-        // sending request to Icing.
-        if (!rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes)) {
-            return new SearchResultPage(Bundle.EMPTY);
-        }
+        rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
 
         ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
         SearchResultProto searchResultProto =
@@ -607,6 +672,31 @@
         }
     }
 
+    /** Reports a usage of the given document at the given timestamp. */
+    public void reportUsage(
+            @NonNull String packageName,
+            @NonNull String databaseName,
+            @NonNull String namespace,
+            @NonNull String uri,
+            long usageTimestampMillis)
+            throws AppSearchException {
+        String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
+        UsageReport report =
+                UsageReport.newBuilder()
+                        .setDocumentNamespace(prefixedNamespace)
+                        .setDocumentUri(uri)
+                        .setUsageTimestampMs(usageTimestampMillis)
+                        .setUsageType(UsageReport.UsageType.USAGE_TYPE1)
+                        .build();
+        mReadWriteLock.writeLock().lock();
+        try {
+            ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report);
+            checkSuccess(result.getStatus());
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
     /**
      * Removes the given document by URI.
      *
@@ -667,12 +757,14 @@
         DeleteByQueryResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            // Only rewrite SearchSpec for non empty prefixes.
-            // rewriteSearchSpecForPrefixesLocked will return false for empty prefixes, we
-            // should skip sending request to Icing and return in here.
+            String prefix = createPrefix(packageName, databaseName);
+            Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemas(prefix, searchSpec);
+
+            // rewriteSearchSpecForPrefixesLocked will return false if there is nothing to search
+            // over given their search filters, so we can return early and skip sending request
+            // to Icing.
             if (!rewriteSearchSpecForPrefixesLocked(
-                    searchSpecBuilder,
-                    Collections.singleton(createPrefix(packageName, databaseName)))) {
+                    searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) {
                 return;
             }
             deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
@@ -904,19 +996,23 @@
     }
 
     /**
-     * Rewrites the schemaTypeFilters and namespacesFilters that exist with {@code prefixes}.
-     *
-     * <p>If the searchSpec has empty filter lists, all prefixes filters will be added.
+     * Rewrites the search spec filters with {@code prefixes}.
      *
      * <p>This method should be only called in query methods and get the READ lock to keep thread
      * safety.
      *
-     * @return false if none of the requested prefixes exist.
+     * @param searchSpecBuilder Client-provided SearchSpec
+     * @param prefixes Prefixes that we should prepend to all our filters
+     * @param allowedPrefixedSchemas Prefixed schemas that the client is allowed to query over. This
+     *     supersedes the schema filters that may exist on the {@code searchSpecBuilder}.
+     * @return false if none there would be nothing to search over.
      */
     @VisibleForTesting
     @GuardedBy("mReadWriteLock")
     boolean rewriteSearchSpecForPrefixesLocked(
-            @NonNull SearchSpecProto.Builder searchSpecBuilder, @NonNull Set<String> prefixes) {
+            @NonNull SearchSpecProto.Builder searchSpecBuilder,
+            @NonNull Set<String> prefixes,
+            @NonNull Set<String> allowedPrefixedSchemas) {
         // Create a copy since retainAll() modifies the original set.
         Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
         existingPrefixes.retainAll(prefixes);
@@ -926,29 +1022,28 @@
             return false;
         }
 
-        // Cache the schema type filters and namespaces before clearing everything.
-        List<String> schemaTypeFilters = searchSpecBuilder.getSchemaTypeFiltersList();
-        searchSpecBuilder.clearSchemaTypeFilters();
+        if (allowedPrefixedSchemas.isEmpty()) {
+            // Not allowed to search over any schemas, empty query.
+            return false;
+        }
 
+        // Clear the schema type filters since we'll be rewriting them with the
+        // allowedPrefixedSchemas.
+        searchSpecBuilder.clearSchemaTypeFilters();
+        searchSpecBuilder.addAllSchemaTypeFilters(allowedPrefixedSchemas);
+
+        // Cache the namespaces before clearing everything.
         List<String> namespaceFilters = searchSpecBuilder.getNamespaceFiltersList();
         searchSpecBuilder.clearNamespaceFilters();
 
-        // Rewrite filters to include a prefix.
+        // Rewrite non-schema filters to include a prefix.
         for (String prefix : existingPrefixes) {
-            Set<String> existingSchemaTypes = mSchemaMapLocked.get(prefix);
-            if (schemaTypeFilters.isEmpty()) {
-                // Include all schema types
-                searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes);
-            } else {
-                // Add the prefix to the given schema types
-                for (int i = 0; i < schemaTypeFilters.size(); i++) {
-                    String prefixedType = prefix + schemaTypeFilters.get(i);
-                    if (existingSchemaTypes.contains(prefixedType)) {
-                        searchSpecBuilder.addSchemaTypeFilters(prefixedType);
-                    }
-                }
-            }
+            // TODO(b/169883602): We currently grab every namespace for every prefix. We can
+            //  optimize this by checking if a prefix has any allowedSchemaTypes. If not, that
+            //  means we don't want to query over anything in that prefix anyways, so we don't
+            //  need to grab its namespaces either.
 
+            // Empty namespaces on the search spec means to query over all namespaces.
             Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix);
             if (namespaceFilters.isEmpty()) {
                 // Include all namespaces
@@ -968,43 +1063,70 @@
     }
 
     /**
+     * Returns the set of allowed prefixed schemas that the {@code prefix} can query while taking
+     * into account the {@code searchSpec} schema filters.
+     *
+     * <p>This only checks intersection of schema filters on the search spec with those that the
+     * prefix owns itself. This does not check global query permissions.
+     */
+    private Set<String> getAllowedPrefixSchemas(
+            @NonNull String prefix, @NonNull SearchSpec searchSpec) {
+        Set<String> allowedPrefixedSchemas = new ArraySet<>();
+
+        // Add all the schema filters the client specified.
+        List<String> schemaFilters = searchSpec.getSchemaTypes();
+        for (int i = 0; i < schemaFilters.size(); i++) {
+            allowedPrefixedSchemas.add(prefix + schemaFilters.get(i));
+        }
+
+        if (allowedPrefixedSchemas.isEmpty()) {
+            // If the client didn't specify any schema filters, search over all of their schemas
+            Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix);
+            if (prefixedSchemas != null) {
+                allowedPrefixedSchemas.addAll(prefixedSchemas);
+            }
+        }
+        return allowedPrefixedSchemas;
+    }
+
+    /**
      * Rewrites the typePropertyMasks that exist in {@code prefixes}.
      *
      * <p>This method should be only called in query methods and get the READ lock to keep thread
      * safety.
      *
-     * @return false if none of the requested prefixes exist.
+     * @param resultSpecBuilder ResultSpecs as specified by client
+     * @param prefixes Prefixes that we should prepend to all our filters
+     * @param allowedPrefixedSchemas Prefixed schemas that the client is allowed to query over.
      */
     @VisibleForTesting
     @GuardedBy("mReadWriteLock")
-    boolean rewriteResultSpecForPrefixesLocked(
-            @NonNull ResultSpecProto.Builder resultSpecBuilder, @NonNull Set<String> prefixes) {
+    void rewriteResultSpecForPrefixesLocked(
+            @NonNull ResultSpecProto.Builder resultSpecBuilder,
+            @NonNull Set<String> prefixes,
+            @NonNull Set<String> allowedPrefixedSchemas) {
         // Create a copy since retainAll() modifies the original set.
         Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
         existingPrefixes.retainAll(prefixes);
 
-        if (existingPrefixes.isEmpty()) {
-            // None of the prefixes exist, empty query.
-            return false;
-        }
-
         List<TypePropertyMask> prefixedTypePropertyMasks = new ArrayList<>();
         // Rewrite filters to include a database prefix.
         for (String prefix : existingPrefixes) {
-            Set<String> existingSchemaTypes = mSchemaMapLocked.get(prefix);
             // Qualify the given schema types
             for (TypePropertyMask typePropertyMask : resultSpecBuilder.getTypePropertyMasksList()) {
-                String qualifiedType = prefix + typePropertyMask.getSchemaType();
-                if (existingSchemaTypes.contains(qualifiedType)) {
+                String unprefixedType = typePropertyMask.getSchemaType();
+                boolean isWildcard =
+                        unprefixedType.equals(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD);
+                String prefixedType = isWildcard ? unprefixedType : prefix + unprefixedType;
+                if (isWildcard || allowedPrefixedSchemas.contains(prefixedType)) {
                     prefixedTypePropertyMasks.add(
-                            typePropertyMask.toBuilder().setSchemaType(qualifiedType).build());
+                            typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
                 }
             }
         }
         resultSpecBuilder
                 .clearTypePropertyMasks()
                 .addAllTypePropertyMasks(prefixedTypePropertyMasks);
-        return true;
     }
 
     @VisibleForTesting
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
index a940ec1..24d64a0 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
@@ -22,10 +22,14 @@
 import android.app.appsearch.GenericDocument;
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.exceptions.AppSearchException;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.RequiresApi;
+
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
@@ -55,7 +59,7 @@
  */
 class VisibilityStore {
     /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
-    @VisibleForTesting static final String VISIBILITY_TYPE = "VisibilityType";
+    private static final String VISIBILITY_TYPE = "VisibilityType";
 
     /**
      * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
@@ -81,15 +85,14 @@
     private static final AppSearchSchema VISIBILITY_SCHEMA =
             new AppSearchSchema.Builder(VISIBILITY_TYPE)
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(
+                            new AppSearchSchema.StringPropertyConfig.Builder(
                                             NOT_PLATFORM_SURFACEABLE_PROPERTY)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
                                     .build())
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(PACKAGE_ACCESSIBLE_PROPERTY)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
+                            new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                            PACKAGE_ACCESSIBLE_PROPERTY)
                                     .setSchemaType(PACKAGE_ACCESSIBLE_TYPE)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
@@ -103,22 +106,20 @@
     private static final AppSearchSchema PACKAGE_ACCESSIBLE_SCHEMA =
             new AppSearchSchema.Builder(PACKAGE_ACCESSIBLE_TYPE)
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
+                            new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
                                     .build())
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(SHA_256_CERT_PROPERTY)
+                            new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
                                     .build())
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(ACCESSIBLE_SCHEMA_PROPERTY)
+                            new AppSearchSchema.StringPropertyConfig.Builder(
+                                            ACCESSIBLE_SCHEMA_PROPERTY)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
                                     .build())
                     .build();
 
@@ -148,6 +149,12 @@
 
     private final AppSearchImpl mAppSearchImpl;
 
+    private final Context mContext;
+
+    // UID of the package that has platform-query privileges, i.e. can query for all
+    // platform-surfaceable content.
+    private int mGlobalQuerierPackageUid;
+
     /**
      * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas
      * in the map are prefixed.
@@ -173,8 +180,20 @@
      *
      * @param appSearchImpl AppSearchImpl instance
      */
-    VisibilityStore(@NonNull AppSearchImpl appSearchImpl) {
+    VisibilityStore(
+            @NonNull AppSearchImpl appSearchImpl,
+            @NonNull Context context,
+            @NonNull String globalQuerierPackage) {
         mAppSearchImpl = appSearchImpl;
+        mContext = context;
+        mGlobalQuerierPackageUid = Process.INVALID_UID;
+
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+            // This should always pass since we should only allow platform access on S+ (the first
+            // version that AppSearch is offered on).
+            mGlobalQuerierPackageUid =
+                    Api24Impl.getGlobalQuerierPackageUid(context, globalQuerierPackage);
+        }
     }
 
     /**
@@ -216,7 +235,8 @@
                                 PACKAGE_NAME,
                                 DATABASE_NAME,
                                 NAMESPACE,
-                                /*uri=*/ addUriPrefix(prefix));
+                                /*uri=*/ addUriPrefix(prefix),
+                                /*typePropertyPaths=*/ Collections.emptyMap());
 
                 // Update platform visibility settings
                 String[] schemas =
@@ -302,8 +322,7 @@
                 schemasPackageAccessible.entrySet()) {
             for (int i = 0; i < entry.getValue().size(); i++) {
                 // TODO(b/169883602): remove the "placeholder" uri once upstream changes to relax
-                // nested
-                // document uri rules gets synced down.
+                // nested document uri rules gets synced down.
                 GenericDocument packageAccessibleDocument =
                         new GenericDocument.Builder(/*uri=*/ "placeholder", PACKAGE_ACCESSIBLE_TYPE)
                                 .setNamespace(NAMESPACE)
@@ -332,42 +351,96 @@
         mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
     }
 
-    /** Returns if the schema is surfaceable by the platform. */
-    // TODO(b/169883602): check permissions against the allowlisted global querier package name.
-    public boolean isSchemaPlatformSurfaceable(
-            @NonNull String prefix, @NonNull String prefixedSchema) {
+    /** Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. */
+    public boolean isSchemaSearchableByCaller(
+            @NonNull String prefix, @NonNull String prefixedSchema, int callerUid) {
         Preconditions.checkNotNull(prefix);
         Preconditions.checkNotNull(prefixedSchema);
-        Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix);
-        if (notPlatformSurfaceableSchemas == null) {
+
+        if (callerUid == mGlobalQuerierPackageUid
+                && isSchemaPlatformSurfaceable(prefix, prefixedSchema)) {
             return true;
         }
+
+        // May not be platform surfaceable, but might still be accessible through 3p access.
+        return isSchemaPackageAccessible(prefix, prefixedSchema, callerUid);
+    }
+
+    /**
+     * Returns whether the caller has platform query privileges, and if so, that the schema is
+     * surfaceable on the platform.
+     */
+    private boolean isSchemaPlatformSurfaceable(
+            @NonNull String prefix, @NonNull String prefixedSchema) {
+        if (prefix.equals(VISIBILITY_STORE_PREFIX)) {
+            // VisibilityStore schemas are for internal bookkeeping.
+            return false;
+        }
+
+        Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix);
+        if (notPlatformSurfaceableSchemas == null) {
+            // No schemas were opted out of being platform-surfaced. So by default, it can be
+            // surfaced.
+            return true;
+        }
+
+        // Some schemas were opted out of being platform-surfaced. As long as this schema
+        // isn't one of those opt-outs, it's surfaceable.
         return !notPlatformSurfaceableSchemas.contains(prefixedSchema);
     }
 
-    /** Returns whether the schema is accessible by {@code accessingPackage}. */
-    // TODO(b/169883602): check certificate and package against the incoming querier's uid/package.
-    public boolean isSchemaPackageAccessible(
-            @NonNull String prefix,
-            @NonNull String prefixedSchema,
-            @NonNull PackageIdentifier accessingPackage) {
-        Preconditions.checkNotNull(prefix);
-        Preconditions.checkNotNull(prefixedSchema);
-        Preconditions.checkNotNull(accessingPackage);
-
+    /**
+     * Returns whether the schema is accessible by the {@code callerUid}. Checks that the callerUid
+     * has one of the allowed PackageIdentifier's package. And if so, that the package also has the
+     * matching certificate.
+     *
+     * <p>This supports packages that have certificate rotation. As long as the specified
+     * certificate was once used to sign the package, the package will still be granted access. This
+     * does not handle packages that have been signed by multiple certificates.
+     */
+    private boolean isSchemaPackageAccessible(
+            @NonNull String prefix, @NonNull String prefixedSchema, int callerUid) {
         Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap =
                 mPackageAccessibleMap.get(prefix);
         if (schemaToPackageIdentifierMap == null) {
+            // No schemas under this prefix have granted package access, return early.
             return false;
         }
 
         Set<PackageIdentifier> packageIdentifiers =
                 schemaToPackageIdentifierMap.get(prefixedSchema);
         if (packageIdentifiers == null) {
+            // No package identifiers were granted access for this schema, return early.
             return false;
         }
 
-        return packageIdentifiers.contains(accessingPackage);
+        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.P) {
+            // PackageManager.hasSigningCertificate is only available on P+
+            // This should never fail since we should only allow package access on S+ (the first
+            // version that AppSearch is offered on). But just in case, default to no package
+            // access.
+            return false;
+        }
+
+        for (PackageIdentifier packageIdentifier : packageIdentifiers) {
+            // Check that the caller uid matches this allowlisted PackageIdentifier.
+            if (Api24Impl.getPackageUid(mContext, packageIdentifier.getPackageName())
+                    != callerUid) {
+                continue;
+            }
+
+            // Check that the package also has the matching certificate
+            if (Api28Impl.hasSigningCertificate(
+                    mContext,
+                    packageIdentifier.getPackageName(),
+                    packageIdentifier.getSha256Certificate())) {
+                // The caller has the right package name and right certificate!
+                return true;
+            }
+        }
+
+        // If we can't verify the schema is package accessible, default to no access.
+        return false;
     }
 
     /**
@@ -389,4 +462,85 @@
     private static String addUriPrefix(String uri) {
         return URI_PREFIX + uri;
     }
+
+    /**
+     * Wrapper class around API 24 methods.
+     *
+     * <p>Even though wrapping a call to a method from an API above minSdk inside an SDK_INT check
+     * makes it runtime safe, it is not optimal. When ART tries to optimize a class, it will do so
+     * regardless of the execution path, and will fail if it tries to resolve a method at a higher
+     * API if that method is being referenced somewhere in the class, even if that method would
+     * never be called at runtime due to the SDK_INT check. ART will however only try to optimize a
+     * class the first time it's referenced at runtime, this means if we wrap our above minSdk
+     * method calls inside classes that are only referenced at runtime at the appropriate API level,
+     * then we guarantee the ability to resolve all the methods.
+     */
+    @RequiresApi(24)
+    private static class Api24Impl {
+        private Api24Impl() {}
+
+        /**
+         * Finds the UID of the {@code globalQuerierPackage}. {@code globalQuerierPackage} must be a
+         * pre-installed, system app. Returns {@link Process#INVALID_UID} if unable to find the UID.
+         */
+        static int getGlobalQuerierPackageUid(
+                @NonNull Context context, @NonNull String globalQuerierPackage) {
+            try {
+                // TODO(b/169883602): In framework, this should be UserHandle.isSameApp or
+                //  packageManager.getPackageUidAsUser().
+                int flags =
+                        PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                                | PackageManager.MATCH_SYSTEM_ONLY;
+                return context.getPackageManager().getPackageUid(globalQuerierPackage, flags);
+            } catch (PackageManager.NameNotFoundException e) {
+                // Global querier doesn't exist.
+            }
+            return Process.INVALID_UID;
+        }
+
+        /**
+         * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable
+         * to find the UID.
+         */
+        static int getPackageUid(@NonNull Context context, @NonNull String packageName) {
+            try {
+                // TODO(b/169883602): In framework, this should be UserHandle.isSameApp or
+                //  packageManager.getPackageUidAsUser().
+                return context.getPackageManager().getPackageUid(packageName, /*flags=*/ 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                // Global querier doesn't exist.
+            }
+            return Process.INVALID_UID;
+        }
+    }
+
+    /**
+     * Wrapper class around API 28 methods.
+     *
+     * <p>Even though wrapping a call to a method from an API above minSdk inside an SDK_INT check
+     * makes it runtime safe, it is not optimal. When ART tries to optimize a class, it will do so
+     * regardless of the execution path, and will fail if it tries to resolve a method at a higher
+     * API if that method is being referenced somewhere in the class, even if that method would
+     * never be called at runtime due to the SDK_INT check. ART will however only try to optimize a
+     * class the first time it's referenced at runtime, this means if we wrap our above minSdk
+     * method calls inside classes that are only referenced at runtime at the appropriate API level,
+     * then we guarantee the ability to resolve all the methods.
+     */
+    @RequiresApi(28)
+    private static class Api28Impl {
+        private Api28Impl() {}
+
+        /**
+         * Returns whether the {@code packageName} has been signed with {@code sha256Certificate}.
+         */
+        static boolean hasSigningCertificate(
+                @NonNull Context context,
+                @NonNull String packageName,
+                @NonNull byte[] sha256Certificate) {
+            return context.getPackageManager()
+                    .hasSigningCertificate(
+                            packageName, sha256Certificate, PackageManager.CERT_INPUT_SHA256);
+        }
+    }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
index 4165af3..ce1c9f4 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.util.Preconditions;
 
+import com.google.android.icing.proto.DocumentIndexingConfig;
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.SchemaTypeConfigProto;
 import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder;
@@ -48,7 +49,9 @@
     public static SchemaTypeConfigProto toSchemaTypeConfigProto(@NonNull AppSearchSchema schema) {
         Preconditions.checkNotNull(schema);
         SchemaTypeConfigProto.Builder protoBuilder =
-                SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType());
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType(schema.getSchemaType())
+                        .setVersion(schema.getVersion());
         List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
         for (int i = 0; i < properties.size(); i++) {
             PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i));
@@ -63,7 +66,6 @@
         Preconditions.checkNotNull(property);
         PropertyConfigProto.Builder builder =
                 PropertyConfigProto.newBuilder().setPropertyName(property.getName());
-        StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder();
 
         // Set dataType
         @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
@@ -74,12 +76,6 @@
         }
         builder.setDataType(dataTypeProto);
 
-        // Set schemaType
-        String schemaType = property.getSchemaType();
-        if (schemaType != null) {
-            builder.setSchemaType(schemaType);
-        }
-
         // Set cardinality
         @AppSearchSchema.PropertyConfig.Cardinality int cardinality = property.getCardinality();
         PropertyConfigProto.Cardinality.Code cardinalityProto =
@@ -89,36 +85,27 @@
         }
         builder.setCardinality(cardinalityProto);
 
-        // Set indexingType
-        @AppSearchSchema.PropertyConfig.IndexingType int indexingType = property.getIndexingType();
-        TermMatchType.Code termMatchTypeProto;
-        switch (indexingType) {
-            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE:
-                termMatchTypeProto = TermMatchType.Code.UNKNOWN;
-                break;
-            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS:
-                termMatchTypeProto = TermMatchType.Code.EXACT_ONLY;
-                break;
-            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES:
-                termMatchTypeProto = TermMatchType.Code.PREFIX;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
-        }
-        indexingConfig.setTermMatchType(termMatchTypeProto);
+        if (property instanceof AppSearchSchema.StringPropertyConfig) {
+            AppSearchSchema.StringPropertyConfig stringProperty =
+                    (AppSearchSchema.StringPropertyConfig) property;
+            StringIndexingConfig stringIndexingConfig =
+                    StringIndexingConfig.newBuilder()
+                            .setTermMatchType(
+                                    convertTermMatchTypeToProto(stringProperty.getIndexingType()))
+                            .setTokenizerType(
+                                    convertTokenizerTypeToProto(stringProperty.getTokenizerType()))
+                            .build();
+            builder.setStringIndexingConfig(stringIndexingConfig);
 
-        // Set tokenizerType
-        @AppSearchSchema.PropertyConfig.TokenizerType
-        int tokenizerType = property.getTokenizerType();
-        StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
-                StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
-        if (tokenizerTypeProto == null) {
-            throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
+        } else if (property instanceof AppSearchSchema.DocumentPropertyConfig) {
+            AppSearchSchema.DocumentPropertyConfig documentProperty =
+                    (AppSearchSchema.DocumentPropertyConfig) property;
+            builder.setSchemaType(documentProperty.getSchemaType())
+                    .setDocumentIndexingConfig(
+                            DocumentIndexingConfig.newBuilder()
+                                    .setIndexNestedProperties(
+                                            documentProperty.isIndexNestedProperties()));
         }
-        indexingConfig.setTokenizerType(tokenizerTypeProto);
-
-        // Build!
-        builder.setStringIndexingConfig(indexingConfig);
         return builder.build();
     }
 
@@ -129,7 +116,8 @@
     @NonNull
     public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) {
         Preconditions.checkNotNull(proto);
-        AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType());
+        AppSearchSchema.Builder builder =
+                new AppSearchSchema.Builder(proto.getSchemaType()).setVersion(proto.getVersion());
         List<PropertyConfigProto> properties = proto.getPropertiesList();
         for (int i = 0; i < properties.size(); i++) {
             AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i));
@@ -142,39 +130,99 @@
     private static AppSearchSchema.PropertyConfig toPropertyConfig(
             @NonNull PropertyConfigProto proto) {
         Preconditions.checkNotNull(proto);
-        AppSearchSchema.PropertyConfig.Builder builder =
-                new AppSearchSchema.PropertyConfig.Builder(proto.getPropertyName())
-                        .setDataType(proto.getDataType().getNumber())
+        switch (proto.getDataType()) {
+            case STRING:
+                return toStringPropertyConfig(proto);
+            case INT64:
+                return new AppSearchSchema.Int64PropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case DOUBLE:
+                return new AppSearchSchema.DoublePropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case BOOLEAN:
+                return new AppSearchSchema.BooleanPropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case BYTES:
+                return new AppSearchSchema.BytesPropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case DOCUMENT:
+                return toDocumentPropertyConfig(proto);
+            default:
+                throw new IllegalArgumentException("Invalid dataType: " + proto.getDataType());
+        }
+    }
+
+    @NonNull
+    private static AppSearchSchema.StringPropertyConfig toStringPropertyConfig(
+            @NonNull PropertyConfigProto proto) {
+        AppSearchSchema.StringPropertyConfig.Builder builder =
+                new AppSearchSchema.StringPropertyConfig.Builder(proto.getPropertyName())
                         .setCardinality(proto.getCardinality().getNumber())
                         .setTokenizerType(
                                 proto.getStringIndexingConfig().getTokenizerType().getNumber());
 
-        // Set schema
-        if (!proto.getSchemaType().isEmpty()) {
-            builder.setSchemaType(proto.getSchemaType());
-        }
-
         // Set indexingType
-        @AppSearchSchema.PropertyConfig.IndexingType int indexingType;
         TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType();
-        switch (termMatchTypeProto) {
+        builder.setIndexingType(convertTermMatchTypeFromProto(termMatchTypeProto));
+
+        return builder.build();
+    }
+
+    @NonNull
+    private static AppSearchSchema.DocumentPropertyConfig toDocumentPropertyConfig(
+            @NonNull PropertyConfigProto proto) {
+        return new AppSearchSchema.DocumentPropertyConfig.Builder(proto.getPropertyName())
+                .setCardinality(proto.getCardinality().getNumber())
+                .setSchemaType(proto.getSchemaType())
+                .setIndexNestedProperties(
+                        proto.getDocumentIndexingConfig().getIndexNestedProperties())
+                .build();
+    }
+
+    @NonNull
+    private static TermMatchType.Code convertTermMatchTypeToProto(
+            @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType) {
+        switch (indexingType) {
+            case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE:
+                return TermMatchType.Code.UNKNOWN;
+            case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS:
+                return TermMatchType.Code.EXACT_ONLY;
+            case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES:
+                return TermMatchType.Code.PREFIX;
+            default:
+                throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
+        }
+    }
+
+    @AppSearchSchema.StringPropertyConfig.IndexingType
+    private static int convertTermMatchTypeFromProto(@NonNull TermMatchType.Code termMatchType) {
+        switch (termMatchType) {
             case UNKNOWN:
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
-                break;
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
             case EXACT_ONLY:
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS;
-                break;
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS;
             case PREFIX:
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES;
-                break;
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES;
             default:
                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
                 // extent possible.
-                Log.w(TAG, "Invalid indexingType: " + termMatchTypeProto.getNumber());
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
+                Log.w(TAG, "Invalid indexingType: " + termMatchType.getNumber());
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
         }
-        builder.setIndexingType(indexingType);
+    }
 
-        return builder.build();
+    @NonNull
+    private static StringIndexingConfig.TokenizerType.Code convertTokenizerTypeToProto(
+            @AppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType) {
+        StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
+                StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
+        if (tokenizerTypeProto == null) {
+            throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
+        }
+        return tokenizerTypeProto;
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
index 0d7d3e1..07d50ae 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
@@ -25,10 +25,6 @@
 import com.google.android.icing.proto.ScoringSpecProto;
 import com.google.android.icing.proto.SearchSpecProto;
 import com.google.android.icing.proto.TermMatchType;
-import com.google.android.icing.proto.TypePropertyMask;
-
-import java.util.List;
-import java.util.Map;
 
 /**
  * Translates a {@link SearchSpec} into icing search protos.
@@ -61,22 +57,17 @@
     @NonNull
     public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
         Preconditions.checkNotNull(spec);
-        ResultSpecProto.Builder builder =
-                ResultSpecProto.newBuilder()
-                        .setNumPerPage(spec.getResultCountPerPage())
-                        .setSnippetSpec(
-                                ResultSpecProto.SnippetSpecProto.newBuilder()
-                                        .setNumToSnippet(spec.getSnippetCount())
-                                        .setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
-                                        .setMaxWindowBytes(spec.getMaxSnippetSize()));
-        Map<String, List<String>> projectionTypePropertyPaths = spec.getProjections();
-        for (Map.Entry<String, List<String>> e : projectionTypePropertyPaths.entrySet()) {
-            builder.addTypePropertyMasks(
-                    TypePropertyMask.newBuilder()
-                            .setSchemaType(e.getKey())
-                            .addAllPaths(e.getValue()));
-        }
-        return builder.build();
+        return ResultSpecProto.newBuilder()
+                .setNumPerPage(spec.getResultCountPerPage())
+                .setSnippetSpec(
+                        ResultSpecProto.SnippetSpecProto.newBuilder()
+                                .setNumToSnippet(spec.getSnippetCount())
+                                .setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
+                                .setMaxWindowBytes(spec.getMaxSnippetSize()))
+                .addAllTypePropertyMasks(
+                        TypePropertyPathToProtoConverter.toTypePropertyMaskList(
+                                spec.getProjections()))
+                .build();
     }
 
     /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
@@ -109,6 +100,10 @@
                 return ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP;
             case SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE:
                 return ScoringSpecProto.RankingStrategy.Code.RELEVANCE_SCORE;
+            case SearchSpec.RANKING_STRATEGY_USAGE_COUNT:
+                return ScoringSpecProto.RankingStrategy.Code.USAGE_TYPE1_COUNT;
+            case SearchSpec.RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP:
+                return ScoringSpecProto.RankingStrategy.Code.USAGE_TYPE1_LAST_USED_TIMESTAMP;
             default:
                 throw new IllegalArgumentException(
                         "Invalid result ranking strategy: " + rankingStrategyCode);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
new file mode 100644
index 0000000..6f6dad2
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage.converter;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.TypePropertyMask;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates a <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>.
+ *
+ * @hide
+ */
+public final class TypePropertyPathToProtoConverter {
+    private TypePropertyPathToProtoConverter() {}
+
+    /** Extracts {@link TypePropertyMask} information from a {@link Map}. */
+    @NonNull
+    public static List<TypePropertyMask> toTypePropertyMaskList(
+            @NonNull Map<String, List<String>> typePropertyPaths) {
+        Preconditions.checkNotNull(typePropertyPaths);
+        List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size());
+        for (Map.Entry<String, List<String>> e : typePropertyPaths.entrySet()) {
+            typePropertyMasks.add(
+                    TypePropertyMask.newBuilder()
+                            .setSchemaType(e.getKey())
+                            .addAllPaths(e.getValue())
+                            .build());
+        }
+        return typePropertyMasks;
+    }
+}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 9be3049..5ab3450 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Icd58005ad659b6b3d03f683f8954939175324685
+Iff96eae150c7cdd281c9ecb5d93f4ef697e89f1a
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index b0478d5..c2c1d7c 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -28,6 +28,7 @@
 import android.app.appsearch.GetByUriRequest;
 import android.app.appsearch.PutDocumentsRequest;
 import android.app.appsearch.RemoveByUriRequest;
+import android.app.appsearch.ReportUsageRequest;
 import android.app.appsearch.SearchResults;
 import android.app.appsearch.SearchResultsShim;
 import android.app.appsearch.SearchSpec;
@@ -129,6 +130,14 @@
 
     @Override
     @NonNull
+    public ListenableFuture<Void> reportUsage(@NonNull ReportUsageRequest request) {
+        SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
+        mAppSearchSession.reportUsage(request, mExecutor, future::set);
+        return Futures.transformAsync(future, this::transformResult, mExecutor);
+    }
+
+    @Override
+    @NonNull
     public ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
             @NonNull RemoveByUriRequest request) {
         SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index 3e81968..ff91f59 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -175,6 +175,25 @@
     SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
 
     /**
+     * Reports usage of a particular document by URI and namespace.
+     *
+     * <p>A usage report represents an event in which a user interacted with or viewed a document.
+     *
+     * <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
+     * metrics for that particular document. These metrics are used for ordering {@link #query}
+     * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link
+     * SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
+     *
+     * <p>Reporting usage of a document is optional.
+     *
+     * @param request The usage reporting request.
+     * @return The pending result of performing this operation which resolves to {@code null} on
+     *     success.
+     */
+    @NonNull
+    ListenableFuture<Void> reportUsage(@NonNull ReportUsageRequest request);
+
+    /**
      * Removes {@link GenericDocument}s from the index by URI.
      *
      * @param request Request containing URIs to be removed.
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 459fd151..13858a3 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
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Future;
 
 public class AppSearchTestUtils {
@@ -59,6 +60,20 @@
         return list;
     }
 
+    public static List<GenericDocument> doGet(AppSearchSessionShim session, GetByUriRequest request)
+            throws Exception {
+        AppSearchBatchResult<String, GenericDocument> result =
+                checkIsBatchResultSuccess(session.getByUri(request));
+        Set<String> uris = request.getUris();
+        assertThat(result.getSuccesses()).hasSize(uris.size());
+        assertThat(result.getFailures()).isEmpty();
+        List<GenericDocument> list = new ArrayList<>(uris.size());
+        for (String uri : uris) {
+            list.add(result.getSuccesses().get(uri));
+        }
+        return list;
+    }
+
     public static List<GenericDocument> convertSearchResultsToDocuments(
             SearchResultsShim searchResults) throws Exception {
         List<SearchResult> results = searchResults.getNextPage().get();
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 7aed32c..c9427e9 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2850,7 +2850,7 @@
         }
         final int appId = UserHandle.getAppId(uid);
         updateTempWhitelistAppIdsLocked(uid, false, 0, 0);
-        mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 0)
+        mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0)
                 .sendToTarget();
         reportTempWhitelistChangedLocked(uid, false);
         try {
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 13c4953..24c9e78 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -237,7 +237,7 @@
      */
     @NonNull
     public List<String> getUnsupportedVideoMimeTypes() {
-        return new ArrayList<>(mSupportedVideoMimeTypes);
+        return new ArrayList<>(mUnsupportedVideoMimeTypes);
     }
 
     /*
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 07221f9..14ebb71 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -62,4 +62,13 @@
     // Create a symlink from app_process to app_process32 or 64
     // depending on the target configuration.
     symlink_preferred_arch: true,
+
+    // Enable ASYNC MTE in the zygote, in order to allow apps and the system
+    // server to use MTE. We use ASYNC because we don't expect the pre-fork
+    // zygote to have substantial memory corruption bugs (as it's primarily Java
+    // code), and we don't want to waste memory recording malloc/free stack
+    // traces (which happens in SYNC mode).
+    sanitize: {
+        memtag_heap: true,
+    },
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index 27660ec..b8327a9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5573,6 +5573,7 @@
     field public static final String EXTRA_PROGRESS = "android.progress";
     field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture";
     field public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
     field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
     field @Deprecated public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
@@ -5712,12 +5713,13 @@
   public static class Notification.BigPictureStyle extends android.app.Notification.Style {
     ctor public Notification.BigPictureStyle();
     ctor @Deprecated public Notification.BigPictureStyle(android.app.Notification.Builder);
-    method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
-    method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
-    method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
+    method @NonNull public android.app.Notification.BigPictureStyle bigLargeIcon(@Nullable android.graphics.Bitmap);
+    method @NonNull public android.app.Notification.BigPictureStyle bigLargeIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.app.Notification.BigPictureStyle bigPicture(@Nullable android.graphics.Bitmap);
     method @NonNull public android.app.Notification.BigPictureStyle bigPictureContentDescription(@Nullable CharSequence);
-    method public android.app.Notification.BigPictureStyle setBigContentTitle(CharSequence);
-    method public android.app.Notification.BigPictureStyle setSummaryText(CharSequence);
+    method @NonNull public android.app.Notification.BigPictureStyle setBigContentTitle(@Nullable CharSequence);
+    method @NonNull public android.app.Notification.BigPictureStyle setSummaryText(@Nullable CharSequence);
+    method @NonNull public android.app.Notification.BigPictureStyle showBigPictureWhenCollapsed(boolean);
   }
 
   public static class Notification.BigTextStyle extends android.app.Notification.Style {
@@ -7191,6 +7193,7 @@
     field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
     field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
     field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+    field public static final String EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY = "android.app.extra.DEVICE_PASSWORD_REQUIREMENT_ONLY";
     field @RequiresPermission(android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY";
     field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
     field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
@@ -21520,6 +21523,8 @@
     method public void removeKeys(@NonNull byte[]);
     method public void removeOfflineLicense(@NonNull byte[]);
     method public void removeSecureStop(@NonNull byte[]);
+    method public boolean requiresSecureDecoder(@NonNull String);
+    method public boolean requiresSecureDecoder(@NonNull String, @android.media.MediaDrm.SecurityLevel int);
     method public void restoreKeys(@NonNull byte[], @NonNull byte[]);
     method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener);
     method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener, @Nullable android.os.Handler);
@@ -31276,6 +31281,7 @@
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectNonSdkApiUsage();
+    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUnsafeIntentLaunch();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
@@ -31284,6 +31290,7 @@
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.StrictMode.OnVmViolationListener);
     method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder permitNonSdkApiUsage();
+    method @NonNull public android.os.StrictMode.VmPolicy.Builder permitUnsafeIntentLaunch();
     method @NonNull public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(Class, int);
   }
 
@@ -31810,6 +31817,9 @@
   public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
   }
 
+  public final class UnsafeIntentLaunchViolation extends android.os.strictmode.Violation {
+  }
+
   public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
   }
 
@@ -33012,6 +33022,7 @@
     field public static final String CACHED_PHOTO_URI = "photo_uri";
     field public static final String CALL_SCREENING_APP_NAME = "call_screening_app_name";
     field public static final String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name";
+    field public static final String COMPOSER_PHOTO_URI = "composer_photo_uri";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
@@ -33036,6 +33047,7 @@
     field public static final String IS_READ = "is_read";
     field public static final String LAST_MODIFIED = "last_modified";
     field public static final String LIMIT_PARAM_KEY = "limit";
+    field public static final String LOCATION = "location";
     field public static final String MISSED_REASON = "missed_reason";
     field public static final long MISSED_REASON_NOT_MISSED = 0L; // 0x0L
     field public static final int MISSED_TYPE = 3; // 0x3
@@ -33051,7 +33063,11 @@
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final String PRIORITY = "priority";
+    field public static final int PRIORITY_NORMAL = 0; // 0x0
+    field public static final int PRIORITY_URGENT = 1; // 0x1
     field public static final int REJECTED_TYPE = 5; // 0x5
+    field public static final String SUBJECT = "subject";
     field public static final String TRANSCRIPTION = "transcription";
     field public static final String TYPE = "type";
     field public static final long USER_MISSED_CALL_FILTERS_TIMEOUT = 4194304L; // 0x400000L
@@ -33066,6 +33082,15 @@
     field public static final String VOICEMAIL_URI = "voicemail_uri";
   }
 
+  public static class CallLog.Locations implements android.provider.BaseColumns {
+    field public static final String AUTHORITY = "call_composer_locations";
+    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_composer_location";
+    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_composer_location";
+    field @NonNull public static final android.net.Uri CONTENT_URI;
+    field public static final String LATITUDE = "latitude";
+    field public static final String LONGITUDE = "longitude";
+  }
+
   @Deprecated public class Contacts {
     field @Deprecated public static final String AUTHORITY = "contacts";
     field @Deprecated public static final android.net.Uri CONTENT_URI;
@@ -39022,7 +39047,9 @@
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
     method public void onStopRtt();
+    method public void onTrackedByNonUiService(boolean);
     method public void onUnhold();
+    method public void onUsingAlternativeUi(boolean);
     method public static String propertiesToString(int);
     method public final void putExtras(@NonNull android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
@@ -39361,6 +39388,7 @@
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+    field public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE = "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE";
     field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
     field public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
     field public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a7602f8..5f61472 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -136,6 +136,7 @@
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     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";
     field public static final String MANAGE_UI_TRANSLATION = "android.permission.MANAGE_UI_TRANSLATION";
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -2344,6 +2345,7 @@
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
     field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
     field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
+    field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
     field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
@@ -4415,7 +4417,8 @@
     method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
     method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
@@ -7329,157 +7332,157 @@
 
 package android.net.metrics {
 
-  public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
+  @Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
   }
 
-  public static final class ApfProgramEvent.Builder {
-    ctor public ApfProgramEvent.Builder();
-    method @NonNull public android.net.metrics.ApfProgramEvent build();
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setLifetime(long);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int);
+  @Deprecated public static final class ApfProgramEvent.Builder {
+    ctor @Deprecated public ApfProgramEvent.Builder();
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent build();
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean);
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int);
   }
 
-  public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
+  @Deprecated public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
   }
 
-  public static final class ApfStats.Builder {
-    ctor public ApfStats.Builder();
-    method @NonNull public android.net.metrics.ApfStats build();
-    method @NonNull public android.net.metrics.ApfStats.Builder setDroppedRas(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setDurationMs(long);
-    method @NonNull public android.net.metrics.ApfStats.Builder setMatchingRas(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setMaxProgramSize(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setParseErrors(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdates(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setReceivedRas(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int);
+  @Deprecated public static final class ApfStats.Builder {
+    ctor @Deprecated public ApfStats.Builder();
+    method @Deprecated @NonNull public android.net.metrics.ApfStats build();
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setDroppedRas(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setDurationMs(long);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setMatchingRas(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setMaxProgramSize(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setParseErrors(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdates(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setReceivedRas(int);
+    method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int);
   }
 
-  public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
+  @Deprecated public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
   }
 
-  public static final class DhcpClientEvent.Builder {
-    ctor public DhcpClientEvent.Builder();
-    method @NonNull public android.net.metrics.DhcpClientEvent build();
-    method @NonNull public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int);
-    method @NonNull public android.net.metrics.DhcpClientEvent.Builder setMsg(String);
+  @Deprecated public static final class DhcpClientEvent.Builder {
+    ctor @Deprecated public DhcpClientEvent.Builder();
+    method @Deprecated @NonNull public android.net.metrics.DhcpClientEvent build();
+    method @Deprecated @NonNull public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int);
+    method @Deprecated @NonNull public android.net.metrics.DhcpClientEvent.Builder setMsg(String);
   }
 
-  public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public DhcpErrorEvent(int);
-    method public static int errorCodeWithOption(int, int);
-    field public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000
-    field public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000
-    field public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000
-    field public static final int DHCP_ERROR = 4; // 0x4
-    field public static final int DHCP_INVALID_OPTION_LENGTH = 67305472; // 0x4030000
-    field public static final int DHCP_NO_COOKIE = 67502080; // 0x4060000
-    field public static final int DHCP_NO_MSG_TYPE = 67371008; // 0x4040000
-    field public static final int DHCP_UNKNOWN_MSG_TYPE = 67436544; // 0x4050000
-    field public static final int L2_ERROR = 1; // 0x1
-    field public static final int L2_TOO_SHORT = 16842752; // 0x1010000
-    field public static final int L2_WRONG_ETH_TYPE = 16908288; // 0x1020000
-    field public static final int L3_ERROR = 2; // 0x2
-    field public static final int L3_INVALID_IP = 33751040; // 0x2030000
-    field public static final int L3_NOT_IPV4 = 33685504; // 0x2020000
-    field public static final int L3_TOO_SHORT = 33619968; // 0x2010000
-    field public static final int L4_ERROR = 3; // 0x3
-    field public static final int L4_NOT_UDP = 50397184; // 0x3010000
-    field public static final int L4_WRONG_PORT = 50462720; // 0x3020000
-    field public static final int MISC_ERROR = 5; // 0x5
-    field public static final int PARSING_ERROR = 84082688; // 0x5030000
-    field public static final int RECEIVE_ERROR = 84017152; // 0x5020000
+  @Deprecated public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor @Deprecated public DhcpErrorEvent(int);
+    method @Deprecated public static int errorCodeWithOption(int, int);
+    field @Deprecated public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000
+    field @Deprecated public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000
+    field @Deprecated public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000
+    field @Deprecated public static final int DHCP_ERROR = 4; // 0x4
+    field @Deprecated public static final int DHCP_INVALID_OPTION_LENGTH = 67305472; // 0x4030000
+    field @Deprecated public static final int DHCP_NO_COOKIE = 67502080; // 0x4060000
+    field @Deprecated public static final int DHCP_NO_MSG_TYPE = 67371008; // 0x4040000
+    field @Deprecated public static final int DHCP_UNKNOWN_MSG_TYPE = 67436544; // 0x4050000
+    field @Deprecated public static final int L2_ERROR = 1; // 0x1
+    field @Deprecated public static final int L2_TOO_SHORT = 16842752; // 0x1010000
+    field @Deprecated public static final int L2_WRONG_ETH_TYPE = 16908288; // 0x1020000
+    field @Deprecated public static final int L3_ERROR = 2; // 0x2
+    field @Deprecated public static final int L3_INVALID_IP = 33751040; // 0x2030000
+    field @Deprecated public static final int L3_NOT_IPV4 = 33685504; // 0x2020000
+    field @Deprecated public static final int L3_TOO_SHORT = 33619968; // 0x2010000
+    field @Deprecated public static final int L4_ERROR = 3; // 0x3
+    field @Deprecated public static final int L4_NOT_UDP = 50397184; // 0x3010000
+    field @Deprecated public static final int L4_WRONG_PORT = 50462720; // 0x3020000
+    field @Deprecated public static final int MISC_ERROR = 5; // 0x5
+    field @Deprecated public static final int PARSING_ERROR = 84082688; // 0x5030000
+    field @Deprecated public static final int RECEIVE_ERROR = 84017152; // 0x5020000
   }
 
-  public class IpConnectivityLog {
-    ctor public IpConnectivityLog();
-    method public boolean log(long, @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(@NonNull String, @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(@NonNull android.net.Network, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(int, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(@NonNull android.net.metrics.IpConnectivityLog.Event);
+  @Deprecated public class IpConnectivityLog {
+    ctor @Deprecated public IpConnectivityLog();
+    method @Deprecated public boolean log(long, @NonNull android.net.metrics.IpConnectivityLog.Event);
+    method @Deprecated public boolean log(@NonNull String, @NonNull android.net.metrics.IpConnectivityLog.Event);
+    method @Deprecated public boolean log(@NonNull android.net.Network, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event);
+    method @Deprecated public boolean log(int, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event);
+    method @Deprecated public boolean log(@NonNull android.net.metrics.IpConnectivityLog.Event);
   }
 
-  public static interface IpConnectivityLog.Event extends android.os.Parcelable {
+  @Deprecated public static interface IpConnectivityLog.Event extends android.os.Parcelable {
   }
 
-  public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public IpManagerEvent(int, long);
-    field public static final int COMPLETE_LIFECYCLE = 3; // 0x3
-    field public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
-    field public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
-    field public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6
-    field public static final int ERROR_STARTING_IPV4 = 4; // 0x4
-    field public static final int ERROR_STARTING_IPV6 = 5; // 0x5
-    field public static final int PROVISIONING_FAIL = 2; // 0x2
-    field public static final int PROVISIONING_OK = 1; // 0x1
+  @Deprecated public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor @Deprecated public IpManagerEvent(int, long);
+    field @Deprecated public static final int COMPLETE_LIFECYCLE = 3; // 0x3
+    field @Deprecated public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
+    field @Deprecated public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
+    field @Deprecated public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6
+    field @Deprecated public static final int ERROR_STARTING_IPV4 = 4; // 0x4
+    field @Deprecated public static final int ERROR_STARTING_IPV6 = 5; // 0x5
+    field @Deprecated public static final int PROVISIONING_FAIL = 2; // 0x2
+    field @Deprecated public static final int PROVISIONING_OK = 1; // 0x1
   }
 
-  public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public IpReachabilityEvent(int);
-    field public static final int NUD_FAILED = 512; // 0x200
-    field public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
-    field public static final int PROBE = 256; // 0x100
-    field public static final int PROVISIONING_LOST = 768; // 0x300
-    field public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500
+  @Deprecated public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor @Deprecated public IpReachabilityEvent(int);
+    field @Deprecated public static final int NUD_FAILED = 512; // 0x200
+    field @Deprecated public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
+    field @Deprecated public static final int PROBE = 256; // 0x100
+    field @Deprecated public static final int PROVISIONING_LOST = 768; // 0x300
+    field @Deprecated public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500
   }
 
-  public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public NetworkEvent(int, long);
-    ctor public NetworkEvent(int);
-    field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
-    field public static final int NETWORK_CONNECTED = 1; // 0x1
-    field public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
-    field public static final int NETWORK_DISCONNECTED = 7; // 0x7
-    field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
-    field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
-    field public static final int NETWORK_LINGER = 5; // 0x5
-    field public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd
-    field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
-    field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
-    field public static final int NETWORK_UNLINGER = 6; // 0x6
-    field public static final int NETWORK_VALIDATED = 2; // 0x2
-    field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
+  @Deprecated public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor @Deprecated public NetworkEvent(int, long);
+    ctor @Deprecated public NetworkEvent(int);
+    field @Deprecated public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
+    field @Deprecated public static final int NETWORK_CONNECTED = 1; // 0x1
+    field @Deprecated public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
+    field @Deprecated public static final int NETWORK_DISCONNECTED = 7; // 0x7
+    field @Deprecated public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
+    field @Deprecated public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
+    field @Deprecated public static final int NETWORK_LINGER = 5; // 0x5
+    field @Deprecated public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd
+    field @Deprecated public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
+    field @Deprecated public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
+    field @Deprecated public static final int NETWORK_UNLINGER = 6; // 0x6
+    field @Deprecated public static final int NETWORK_VALIDATED = 2; // 0x2
+    field @Deprecated public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
   }
 
-  public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
+  @Deprecated public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
   }
 
-  public static final class RaEvent.Builder {
-    ctor public RaEvent.Builder();
-    method @NonNull public android.net.metrics.RaEvent build();
-    method @NonNull public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updateRouterLifetime(long);
+  @Deprecated public static final class RaEvent.Builder {
+    ctor @Deprecated public RaEvent.Builder();
+    method @Deprecated @NonNull public android.net.metrics.RaEvent build();
+    method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long);
+    method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateRouterLifetime(long);
   }
 
-  public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
-    method @NonNull public static String getProbeName(int);
-    field public static final int DNS_FAILURE = 0; // 0x0
-    field public static final int DNS_SUCCESS = 1; // 0x1
-    field public static final int PROBE_DNS = 0; // 0x0
-    field public static final int PROBE_FALLBACK = 4; // 0x4
-    field public static final int PROBE_HTTP = 1; // 0x1
-    field public static final int PROBE_HTTPS = 2; // 0x2
-    field public static final int PROBE_PAC = 3; // 0x3
-    field public static final int PROBE_PRIVDNS = 5; // 0x5
+  @Deprecated public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
+    method @Deprecated @NonNull public static String getProbeName(int);
+    field @Deprecated public static final int DNS_FAILURE = 0; // 0x0
+    field @Deprecated public static final int DNS_SUCCESS = 1; // 0x1
+    field @Deprecated public static final int PROBE_DNS = 0; // 0x0
+    field @Deprecated public static final int PROBE_FALLBACK = 4; // 0x4
+    field @Deprecated public static final int PROBE_HTTP = 1; // 0x1
+    field @Deprecated public static final int PROBE_HTTPS = 2; // 0x2
+    field @Deprecated public static final int PROBE_PAC = 3; // 0x3
+    field @Deprecated public static final int PROBE_PRIVDNS = 5; // 0x5
   }
 
-  public static final class ValidationProbeEvent.Builder {
-    ctor public ValidationProbeEvent.Builder();
-    method @NonNull public android.net.metrics.ValidationProbeEvent build();
-    method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long);
-    method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean);
-    method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int);
+  @Deprecated public static final class ValidationProbeEvent.Builder {
+    ctor @Deprecated public ValidationProbeEvent.Builder();
+    method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent build();
+    method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long);
+    method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean);
+    method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int);
   }
 
 }
@@ -7752,7 +7755,10 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnEnabled();
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnSupported();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setAlwaysOn(boolean);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
     field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
   }
@@ -9742,6 +9748,7 @@
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
     method public void onAllowedAdjustmentsChanged();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
+    method public void onNotificationClicked(@NonNull String);
     method public void onNotificationDirectReplied(@NonNull String);
     method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
     method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9e83136..870ab83 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -292,6 +292,7 @@
   }
 
   public class StatusBarManager {
+    method public void clickNotification(@Nullable String, int, int, boolean);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
     method public void expandNotificationsPanel();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
@@ -727,6 +728,11 @@
     method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
   }
 
+  public final class Rect implements android.os.Parcelable {
+    method public void splitHorizontally(@NonNull android.graphics.Rect...);
+    method public void splitVertically(@NonNull android.graphics.Rect...);
+  }
+
 }
 
 package android.graphics.drawable {
@@ -1182,7 +1188,7 @@
 package android.net {
 
   public class ConnectivityManager {
-    method @RequiresPermission(anyOf={"android.permission.MANAGE_TEST_NETWORKS", android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
   }
 
   public class EthernetManager {
@@ -2560,6 +2566,7 @@
     method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
     method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
     method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
+    method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
     method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
     method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
     method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 986051c..9b5a1dd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -532,4 +532,9 @@
      */
     @Nullable
     public abstract Intent getIntentForIntentSender(IIntentSender sender);
+
+    /**
+     * @return mBootTimeTempAllowlistDuration of ActivityManagerConstants.
+     */
+    public abstract long getBootTimeTempAllowListDuration();
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd437f4..e5c34c5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -64,12 +64,14 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ProviderInfoList;
 import android.content.pm.ServiceInfo;
@@ -346,6 +348,7 @@
     private int mPendingProcessState = PROCESS_STATE_UNKNOWN;
     ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>();
     private int mLastSessionId;
+    final ArrayMap<IBinder, CreateServiceData> mServicesData = new ArrayMap<>();
     @UnsupportedAppUsage
     final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
     @UnsupportedAppUsage
@@ -3412,7 +3415,7 @@
                     cl, component.getClassName(), r.intent);
             StrictMode.incrementExpectedActivityCount(activity.getClass());
             r.intent.setExtrasClassLoader(cl);
-            r.intent.prepareToEnterProcess();
+            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
             if (r.state != null) {
                 r.state.setClassLoader(cl);
             }
@@ -3717,7 +3720,7 @@
         for (int i=0; i<N; i++) {
             ReferrerIntent intent = intents.get(i);
             intent.setExtrasClassLoader(r.activity.getClassLoader());
-            intent.prepareToEnterProcess();
+            intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
             r.activity.mFragments.noteStateNotSaved();
             mInstrumentation.callActivityOnNewIntent(r.activity, intent);
         }
@@ -4052,7 +4055,8 @@
             }
             java.lang.ClassLoader cl = context.getClassLoader();
             data.intent.setExtrasClassLoader(cl);
-            data.intent.prepareToEnterProcess();
+            data.intent.prepareToEnterProcess(
+                    isProtectedComponent(data.info) || isProtectedBroadcast(data.intent));
             data.setExtrasClassLoader(cl);
             receiver = packageInfo.getAppFactory()
                     .instantiateReceiver(cl, data.info.name, data.intent);
@@ -4249,6 +4253,7 @@
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             service.onCreate();
+            mServicesData.put(data.token, data);
             mServices.put(data.token, service);
             try {
                 ActivityManager.getService().serviceDoneExecuting(
@@ -4266,13 +4271,14 @@
     }
 
     private void handleBindService(BindServiceData data) {
+        CreateServiceData createData = mServicesData.get(data.token);
         Service s = mServices.get(data.token);
         if (DEBUG_SERVICE)
             Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
         if (s != null) {
             try {
                 data.intent.setExtrasClassLoader(s.getClassLoader());
-                data.intent.prepareToEnterProcess();
+                data.intent.prepareToEnterProcess(isProtectedComponent(createData.info));
                 try {
                     if (!data.rebind) {
                         IBinder binder = s.onBind(data.intent);
@@ -4297,11 +4303,12 @@
     }
 
     private void handleUnbindService(BindServiceData data) {
+        CreateServiceData createData = mServicesData.get(data.token);
         Service s = mServices.get(data.token);
         if (s != null) {
             try {
                 data.intent.setExtrasClassLoader(s.getClassLoader());
-                data.intent.prepareToEnterProcess();
+                data.intent.prepareToEnterProcess(isProtectedComponent(createData.info));
                 boolean doRebind = s.onUnbind(data.intent);
                 try {
                     if (doRebind) {
@@ -4373,12 +4380,13 @@
     }
 
     private void handleServiceArgs(ServiceArgsData data) {
+        CreateServiceData createData = mServicesData.get(data.token);
         Service s = mServices.get(data.token);
         if (s != null) {
             try {
                 if (data.args != null) {
                     data.args.setExtrasClassLoader(s.getClassLoader());
-                    data.args.prepareToEnterProcess();
+                    data.args.prepareToEnterProcess(isProtectedComponent(createData.info));
                 }
                 int res;
                 if (!data.taskRemoved) {
@@ -4407,6 +4415,7 @@
     }
 
     private void handleStopService(IBinder token) {
+        mServicesData.remove(token);
         Service s = mServices.remove(token);
         if (s != null) {
             try {
@@ -5026,7 +5035,7 @@
             try {
                 if (ri.mData != null) {
                     ri.mData.setExtrasClassLoader(r.activity.getClassLoader());
-                    ri.mData.prepareToEnterProcess();
+                    ri.mData.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
                 }
                 if (DEBUG_RESULTS) Slog.v(TAG,
                         "Delivering result to activity " + r + " : " + ri);
@@ -7739,6 +7748,66 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
+    /**
+     * Returns whether the provided {@link ActivityInfo} {@code ai} is a protected component.
+     *
+     * @see #isProtectedComponent(ComponentInfo, String)
+     */
+    public static boolean isProtectedComponent(@NonNull ActivityInfo ai) {
+        return isProtectedComponent(ai, ai.permission);
+    }
+
+    /**
+     * Returns whether the provided {@link ServiceInfo} {@code si} is a protected component.
+     *
+     * @see #isProtectedComponent(ComponentInfo, String)
+     */
+    public static boolean isProtectedComponent(@NonNull ServiceInfo si) {
+        return isProtectedComponent(si, si.permission);
+    }
+
+    /**
+     * Returns whether the provided {@link ComponentInfo} {@code ci} with the specified {@code
+     * permission} is a protected component.
+     *
+     * <p>A component is protected if it is not exported, or if the specified {@code permission} is
+     * a signature permission.
+     */
+    private static boolean isProtectedComponent(@NonNull ComponentInfo ci,
+            @Nullable String permission) {
+        // Bail early when this process isn't looking for violations
+        if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false;
+
+        // TODO: consider optimizing by having AMS pre-calculate this value
+        if (!ci.exported) {
+            return true;
+        }
+        if (permission != null) {
+            try {
+                PermissionInfo pi = getPermissionManager().getPermissionInfo(permission,
+                        currentOpPackageName(), 0);
+                return (pi != null) && pi.getProtection() == PermissionInfo.PROTECTION_SIGNATURE;
+            } catch (RemoteException ignored) {
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the action within the provided {@code intent} is a protected broadcast.
+     */
+    public static boolean isProtectedBroadcast(@NonNull Intent intent) {
+        // Bail early when this process isn't looking for violations
+        if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false;
+
+        // TODO: consider optimizing by having AMS pre-calculate this value
+        try {
+            return getPackageManager().isProtectedBroadcast(intent.getAction());
+        } catch (RemoteException ignored) {
+        }
+        return false;
+    }
+
     // ------------------ Regular JNI ------------------------
     private native void nPurgePendingResources();
     private native void nDumpGraphicsInfo(FileDescriptor fd);
diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md
index b827a0d..4589a71 100644
--- a/core/java/android/app/AppOps.md
+++ b/core/java/android/app/AppOps.md
@@ -231,7 +231,107 @@
 As each runtime permission has an associated app-op this API is particularly useful for an app
 that want to find unexpected private data accesses.
 
-### Tracking the last accesses via an API
+#### Implementation
+
+The goal is to trigger a callback to `AppOpsManager.OnOpNotedCallback` any time a data provider
+declares that data was sent to the app (i.e. calls `AppOpsManager.noteOp`). There are four cases
+
+##### Synchronous data accesses
+
+This is the case where the client calls an API and the data is sent back as the return value of this
+API call. E.g. `LocationManager.getLastKnownLocation` returns the last known location as the return
+value of the method call.
+
+In this case
+1. The client calls into a Android API in the Android framework, e.g. `LocationManager`
+2. The framework code calls via a `Binder` call into the data provider, e.g. the
+`LocationManagerService` residing in the system server.
+3. Somewhere in the data provider the data provider calls `AppOpsManager.noteOp` and thereby
+declares that data was accessed. This data access is recorded in
+`AppOpsManager.sAppOpsNotedInThisBinderTransaction`
+4. When the binder call returns the RPC code (`Binder`/`Parcel`) calls
+`AppOpsManager.readAndLogNotedAppops` which checks is the binder return value contained any
+prefix indicating that data was accessed. If so the RPC code calls `onNoted` on the the currently
+registered `OnOpNotedCallback`.
+5. The rest of the implementation is up to the client, but one to use the callbacks is for  the
+client to take a stack trace in the `onNoted` implementation. This stack trace allows to pin point
+where in the app's code the data access came from.
+
+![Syncronous data access by a client via a binder call](sync-data-access.png)
+
+In above graphics you can see that
+1. an app (`com.app.A`, red) is calling into the android framework
+(blue).
+1. The call triggers a RPC call into the data provider (green).
+1. The data provider calls `AppOpsManager.noteOp` (first star)
+1. On the return from the RPC call the framework code (second star) realizes that there was a data
+access and calls `OnOpNotedCallback.onNoted`.
+1. If at this time the code in onNoted would take a stack trace it would get what is in the gray
+box, i.e.
+```
+com.app.A.a
+- com.app.A.b
+  - com.app.A.c
+    - com.app.A.d
+       - android...Manager
+         - several android internal RPC methods
+           - com.app.B.onNoted (extends OnOpNotedCallback.onNoted)
+```
+
+As `onNoted` also reports the attributionTag and the noted op the app can now build a mapping
+indicating what code accesses what private data.
+
+##### Self data accesses
+
+This is similar to the [synchronous data access](#synchronous-data-accesses) case only that the data
+provider and client are in the same process. In this case Android's RPC code is no involved and
+`AppOpsManager.noteOp` directly triggers `OnOpNotedCallback.onSelfNoted`. This should be a uncommon
+case as it is uncommon for an app to provide data, esp. to itself.
+
+If an app takes above suggestion and collects stack traces for synchronous accesses self-accesses
+can be treated in the same way.
+
+##### Async data accesses
+
+There are cases where the data access is not directly triggered via an API. E.g.
+`LocationManager.requestLocationUpdates(listener)` registers a callback. Once the location subsystem
+determines a location it calls the registered listener with the data. There can be quite significant
+time between registering the listener and getting the data. In some cases (e.g. Geo-fencing) it
+might take days and the app registering for the data and the app receiving the data might not even
+be the same process or even version.
+
+Hence above suggestion with taking the stack trace to determine what triggered the data access does
+not work. In this case it is recommended for data providers to come up with a way to help the app
+developer understand why a data access is triggered. E.g. in the case of
+`LocationManager.requestLocationUpdates(listener)` the data provider is setting the `message` field
+in`AppOpsManager.noteOp` to the system-identity hash code of the registered listener. There are
+convenience methods for that, e.g. `AppOpsManager.toReceiverId`. This `message` field is then
+delivered to the app inside the `AsyncNotedAppOp` parameter to `OnOpNotedCallback.onAsyncNoted`.
+
+While this case is not as elegant as the synchronous case, a properly set `message` can often be
+enough for the app to figure out where the data access comes from. Async data accesses are less
+common than synchronous data accesses but they come in more variations. E.g. registered listeners,
+pending-intents, manifest broadcast receivers, activity starts, etc... Hence there is no one perfect
+message format. This is why the message field is a free text string.
+
+It is very highly recommended for data providers to set appropriate `message` parameters for their
+`AppOpsManager.noteOp` calls for all times where there is async data access. If no `message`
+parameter is set, the system defaults to a stack trace of the data provider code which is often slow
+and not useful.
+
+Async data accesses also carry the attribution tag, but this can sometimes not be enough. Again, a
+properly set `message` parameter is the best choice.
+
+##### Data providers implemented in native code
+
+Some data providers (e.g. camera a microphone) are implemented using native code. As of now this is
+not properly hooked up to the Java logic. To make sure to always collect all data accesses all
+`AppOpsManager::noteOp` calls from native code trigger an [async data access](#async-data-accesses),
+no matter if the code is in a synchronous RPC or not.
+
+This is not ideal and should be improved.
+
+### Getting last data accesses via an API
 
 To get the last accesses for an op or package an app can use `AppOpsManager.getPackagesForOps`.
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bac5025..5222699 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1670,7 +1670,9 @@
                     flags);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
-                intent.prepareToEnterProcess();
+                // TODO: determine at registration time if caller is
+                // protecting themselves with signature permission
+                intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent));
             }
             return intent;
         } catch (RemoteException e) {
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 301d188..9e1c505 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -214,4 +214,11 @@
      * @param displayId id of the display where activity will rotate
      */
      void onActivityRotation(int displayId);
+
+    /**
+     * Called when a task is moved to the back behind the home stack.
+     *
+     * @param taskInfo info about the task which moved
+     */
+    void onTaskMovedToBack(in ActivityManager.RunningTaskInfo taskInfo);
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 50853a3..c01b5a3 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1617,7 +1617,9 @@
                     try {
                         ClassLoader cl = mReceiver.getClass().getClassLoader();
                         intent.setExtrasClassLoader(cl);
-                        intent.prepareToEnterProcess();
+                        // TODO: determine at registration time if caller is
+                        // protecting themselves with signature permission
+                        intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent));
                         setExtrasClassLoader(cl);
                         receiver.setPendingResult(this);
                         receiver.onReceive(mContext, intent);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f6a06f3..209286c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1198,6 +1198,14 @@
             "android.pictureContentDescription";
 
     /**
+     * {@link #extras} key: this is a boolean to indicate that the
+     * {@link BigPictureStyle#bigPicture(Bitmap) big picture} is to be shown in the collapsed state
+     * of a {@link BigPictureStyle} notification.  This will replace a
+     * {@link Builder#setLargeIcon(Icon) large icon} in that state if one was provided.
+     */
+    public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture";
+
+    /**
      * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
      * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
      */
@@ -5149,6 +5157,7 @@
         // changes are entirely visual, and should otherwise be undetectable by apps.
         @SuppressWarnings("AndroidFrameworkCompatChange")
         private void calculateLargeIconDimens(boolean largeIconShown,
+                @NonNull StandardTemplateParams p,
                 @NonNull TemplateBindResult result) {
             final Resources resources = mContext.getResources();
             final float density = resources.getDisplayMetrics().density;
@@ -5159,9 +5168,9 @@
             final float viewHeightDp = resources.getDimension(
                     R.dimen.notification_right_icon_size) / density;
             float viewWidthDp = viewHeightDp;  // icons are 1:1 by default
-            if (largeIconShown && (
-                    mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S
-                            || DevFlags.shouldBackportSNotifRules(mContext.getContentResolver()))) {
+            if (largeIconShown && (p.mPromotePicture
+                    || mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S
+                    || DevFlags.shouldBackportSNotifRules(mContext.getContentResolver()))) {
                 Drawable drawable = mN.mLargeIcon.loadDrawable(mContext);
                 if (drawable != null) {
                     int iconWidth = drawable.getIntrinsicWidth();
@@ -5187,7 +5196,7 @@
                 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
             }
             boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon;
-            calculateLargeIconDimens(showLargeIcon, result);
+            calculateLargeIconDimens(showLargeIcon, p, result);
             if (showLargeIcon) {
                 contentView.setViewLayoutWidth(R.id.right_icon,
                         result.mRightIconWidthDp, TypedValue.COMPLEX_UNIT_DIP);
@@ -6995,6 +7004,7 @@
         private Icon mBigLargeIcon;
         private boolean mBigLargeIconSet = false;
         private CharSequence mPictureContentDescription;
+        private boolean mPromotePicture;
 
         public BigPictureStyle() {
         }
@@ -7011,7 +7021,8 @@
          * Overrides ContentTitle in the big form of the template.
          * This defaults to the value passed to setContentTitle().
          */
-        public BigPictureStyle setBigContentTitle(CharSequence title) {
+        @NonNull
+        public BigPictureStyle setBigContentTitle(@Nullable CharSequence title) {
             internalSetBigContentTitle(safeCharSequence(title));
             return this;
         }
@@ -7019,7 +7030,8 @@
         /**
          * Set the first line of text after the detail section in the big form of the template.
          */
-        public BigPictureStyle setSummaryText(CharSequence cs) {
+        @NonNull
+        public BigPictureStyle setSummaryText(@Nullable CharSequence cs) {
             internalSetSummaryText(safeCharSequence(cs));
             return this;
         }
@@ -7044,22 +7056,36 @@
         /**
          * Provide the bitmap to be used as the payload for the BigPicture notification.
          */
-        public BigPictureStyle bigPicture(Bitmap b) {
+        @NonNull
+        public BigPictureStyle bigPicture(@Nullable Bitmap b) {
             mPicture = b;
             return this;
         }
 
         /**
+         * When set, the {@link #bigPicture(Bitmap) big picture} of this style will be promoted and
+         * shown in place of the {@link Builder#setLargeIcon(Icon) large icon} in the collapsed
+         * state of this notification.
+         */
+        @NonNull
+        public BigPictureStyle showBigPictureWhenCollapsed(boolean show) {
+            mPromotePicture = show;
+            return this;
+        }
+
+        /**
          * Override the large icon when the big notification is shown.
          */
-        public BigPictureStyle bigLargeIcon(Bitmap b) {
+        @NonNull
+        public BigPictureStyle bigLargeIcon(@Nullable Bitmap b) {
             return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null);
         }
 
         /**
          * Override the large icon when the big notification is shown.
          */
-        public BigPictureStyle bigLargeIcon(Icon icon) {
+        @NonNull
+        public BigPictureStyle bigLargeIcon(@Nullable Icon icon) {
             mBigLargeIconSet = true;
             mBigLargeIcon = icon;
             return this;
@@ -7112,6 +7138,66 @@
         /**
          * @hide
          */
+        @Override
+        public RemoteViews makeContentView(boolean increasedHeight) {
+            if (mPicture == null || !mPromotePicture) {
+                return super.makeContentView(increasedHeight);
+            }
+
+            Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
+            mBuilder.mN.mLargeIcon = Icon.createWithBitmap(mPicture);
+            // The legacy largeIcon might not allow us to clear the image, as it's taken in
+            // replacement if the other one is null. Because we're restoring these legacy icons
+            // for old listeners, this is in general non-null.
+            Bitmap largeIconLegacy = mBuilder.mN.largeIcon;
+            mBuilder.mN.largeIcon = null;
+
+            StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
+                    .fillTextsFrom(mBuilder)
+                    .promotePicture(true);
+            RemoteViews contentView = getStandardView(mBuilder.getBaseLayoutResource(),
+                    p, null /* result */);
+
+            mBuilder.mN.mLargeIcon = oldLargeIcon;
+            mBuilder.mN.largeIcon = largeIconLegacy;
+
+            return contentView;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+            if (mPicture == null || !mPromotePicture) {
+                return super.makeHeadsUpContentView(increasedHeight);
+            }
+
+            Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
+            mBuilder.mN.mLargeIcon = Icon.createWithBitmap(mPicture);
+            // The legacy largeIcon might not allow us to clear the image, as it's taken in
+            // replacement if the other one is null. Because we're restoring these legacy icons
+            // for old listeners, this is in general non-null.
+            Bitmap largeIconLegacy = mBuilder.mN.largeIcon;
+            mBuilder.mN.largeIcon = null;
+
+            StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
+                    .fillTextsFrom(mBuilder)
+                    .promotePicture(true);
+            RemoteViews contentView = getStandardView(mBuilder.getHeadsUpBaseLayoutResource(),
+                    p, null /* result */);
+
+            mBuilder.mN.mLargeIcon = oldLargeIcon;
+            mBuilder.mN.largeIcon = largeIconLegacy;
+
+            return contentView;
+        }
+
+        /**
+         * @hide
+         */
         public RemoteViews makeBigContentView() {
             // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
             // This covers the following cases:
@@ -7168,6 +7254,7 @@
                 extras.putCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION,
                         mPictureContentDescription);
             }
+            extras.putBoolean(EXTRA_PROMOTE_PICTURE, mPromotePicture);
             extras.putParcelable(EXTRA_PICTURE, mPicture);
         }
 
@@ -7188,6 +7275,7 @@
                         extras.getCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION);
             }
 
+            mPromotePicture = extras.getBoolean(EXTRA_PROMOTE_PICTURE);
             mPicture = extras.getParcelable(EXTRA_PICTURE);
         }
 
@@ -8962,22 +9050,9 @@
             return remoteViews;
         }
 
-        // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps,
-        // a use case that is not supported by the Compat Framework library.  Workarounds to resolve
-        // the change's state in NotificationManagerService were very complex. While it's possible
-        // apps can detect the change, it's most likely that the changes will simply result in
-        // visual regressions.
-        @SuppressWarnings("AndroidFrameworkCompatChange")
         private int getDecorationType() {
             ContentResolver contentResolver = mBuilder.mContext.getContentResolver();
-            if (mBuilder.mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S
-                    || DevFlags.shouldBackportSNotifRules(contentResolver)) {
-                return DevFlags.getDecoratedCustomViewNotifDecoration(contentResolver);
-            } else {
-                // For apps that don't target S, this decoration provides the closest behavior to R,
-                // but doesn't fit with the design guidelines for S.
-                return DevFlags.DECORATION_FULL_COMPATIBLE;
-            }
+            return DevFlags.getDecoratedCustomViewNotifDecoration(contentResolver);
         }
 
         /**
@@ -11306,6 +11381,7 @@
         boolean mHideTitle;
         boolean mHideActions;
         boolean mHideProgress;
+        boolean mPromotePicture;
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
@@ -11321,6 +11397,7 @@
             mHideTitle = false;
             mHideActions = false;
             mHideProgress = false;
+            mPromotePicture = false;
             title = null;
             text = null;
             summaryText = null;
@@ -11360,6 +11437,11 @@
             return this;
         }
 
+        final StandardTemplateParams promotePicture(boolean promotePicture) {
+            this.mPromotePicture = promotePicture;
+            return this;
+        }
+
         final StandardTemplateParams title(CharSequence title) {
             this.title = title;
             return this;
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 6d79e2d..60bfac5 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -42,8 +42,10 @@
 # Multiuser
 per-file *User* = file:/MULTIUSER_OWNERS
 
-# Notification
+# Notification, DND, Status bar
 per-file *Notification* = file:/packages/SystemUI/OWNERS
+per-file *Zen* = file:/packages/SystemUI/OWNERS
+per-file *StatusBar* = file:/packages/SystemUI/OWNERS
 
 # ResourcesManager
 per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 602e9a3..269ccb1 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -40,7 +40,6 @@
 import android.os.UserHandle;
 import android.util.AndroidException;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.os.IResultReceiver;
@@ -108,7 +107,6 @@
  * FLAG_ONE_SHOT, <b>both</b> FLAG_ONE_SHOT and FLAG_NO_CREATE need to be supplied.
  */
 public final class PendingIntent implements Parcelable {
-    private static final String TAG = "PendingIntent";
     private final IIntentSender mTarget;
     private IResultReceiver mCancelReceiver;
     private IBinder mWhitelistToken;
@@ -354,7 +352,7 @@
 
         if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
                 && !flagImmutableSet && !flagMutableSet) {
-            Log.e(TAG, msg);
+            throw new IllegalArgumentException(msg);
         }
     }
 
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 4e3d85c..7d2bc1a 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -35,6 +35,7 @@
 import android.view.View;
 
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -263,6 +264,28 @@
     }
 
     /**
+     * Simulate notification click for testing
+     *
+     * @hide
+     */
+    @TestApi
+    public void clickNotification(@Nullable String key, int rank, int count, boolean visible) {
+        clickNotificationInternal(key, rank, count, visible);
+    }
+
+    private void clickNotificationInternal(String key, int rank, int count, boolean visible) {
+        try {
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                svc.onNotificationClick(key,
+                        NotificationVisibility.obtain(key, rank, count, visible));
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Expand the notifications panel.
      *
      * @hide
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index c311d718..d1b544d 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -17,13 +17,13 @@
 package android.app;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.window.TaskSnapshot;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.window.TaskSnapshot;
 
 /**
  * Classes interested in observing only a subset of changes using ITaskStackListener can extend
@@ -196,4 +196,8 @@
     @Override
     public void onActivityRotation(int displayId) {
     }
+
+    @Override
+    public void onTaskMovedToBack(RunningTaskInfo taskInfo) {
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 54e1ac43..7e8fb91 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1686,6 +1686,20 @@
     public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000;
 
     /**
+     * A boolean extra for {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} requesting that only
+     * device password requirement is enforced during the parent profile password enrolment flow.
+     * <p> Normally when enrolling password for the parent profile, both the device-wide password
+     * requirement (requirement set via {@link #getParentProfileInstance(ComponentName)} instance)
+     * and the profile password requirement are enforced, if the profile currently does not have a
+     * separate work challenge. By setting this to {@code true}, profile password requirement is
+     * explicitly disregarded.
+     *
+     * @see #isActivePasswordSufficientForDeviceRequirement()
+     */
+    public static final String EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY =
+            "android.app.extra.DEVICE_PASSWORD_REQUIREMENT_ONLY";
+
+    /**
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -1700,8 +1714,10 @@
     /**
      * Activity action: have the user enter a new password for the parent profile.
      * If the intent is launched from within a managed profile, this will trigger
-     * entering a new password for the parent of the profile. In all other cases
-     * the behaviour is identical to {@link #ACTION_SET_NEW_PASSWORD}.
+     * entering a new password for the parent of the profile. The caller can optionally
+     * set {@link #EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY} to only enforce device-wide
+     * password requirement. In all other cases the behaviour is identical to
+     * {@link #ACTION_SET_NEW_PASSWORD}.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD
@@ -3844,9 +3860,21 @@
      * @hide
      */
     public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle) {
+        return getPasswordMinimumMetrics(userHandle, false);
+    }
+
+    /**
+     * Returns minimum PasswordMetrics that satisfies all admin policies.
+     * If requested, only consider device-wide admin policies and ignore policies set on the
+     * managed profile instance (as if the managed profile had separate work challenge).
+     *
+     * @hide
+     */
+    public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle,
+            boolean deviceWideOnly) {
         if (mService != null) {
             try {
-                return mService.getPasswordMinimumMetrics(userHandle);
+                return mService.getPasswordMinimumMetrics(userHandle, deviceWideOnly);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4125,6 +4153,7 @@
      * @throws SecurityException if the calling application is not a profile owner of a managed
      *   profile, or if this API is not called on the parent DevicePolicyManager instance.
      * @throws IllegalStateException if the user isn't unlocked
+     * @see #EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY
      */
     public boolean isActivePasswordSufficientForDeviceRequirement() {
         if (!mParentInstance) {
@@ -4250,12 +4279,25 @@
      */
     @PasswordComplexity
     public int getAggregatedPasswordComplexityForUser(int userId) {
+        return getAggregatedPasswordComplexityForUser(userId, false);
+    }
+
+    /**
+     * Returns the password complexity that applies to this user, aggregated from other users if
+     * necessary (for example, if the DPC has set password complexity requirements on the parent
+     * profile DPM instance of a managed profile user, they would apply to the primary user on the
+     * device). If {@code deviceWideOnly} is {@code true}, ignore policies set on the
+     * managed profile DPM instance (as if the managed profile had separate work challenge).
+     * @hide
+     */
+    @PasswordComplexity
+    public int getAggregatedPasswordComplexityForUser(int userId, boolean deviceWideOnly) {
         if (mService == null) {
             return PASSWORD_COMPLEXITY_NONE;
         }
 
         try {
-            return mService.getAggregatedPasswordComplexityForUser(userId);
+            return mService.getAggregatedPasswordComplexityForUser(userId, deviceWideOnly);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -8633,6 +8675,19 @@
      * Called by a profile or device owner to set the permitted input methods services for this
      * user. By default, the user can use any input method.
      * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance,
+     * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
+     * a profile owner of an organization-owned device.
+     * <p>
+     * If called on the parent instance:
+     * <ul>
+     *    <li>The permitted input methods will be applied on the personal profile</li>
+     *    <li>Can only permit all input methods (calling this method with a {@code null} package
+     *    list) or only permit system input methods (calling this method with an empty package
+     *    list). This is to prevent the caller from learning which packages are installed on
+     *    the personal side</li>
+     * </ul>
+     * <p>
      * When zero or more packages have been added, input method that are not in the list and not
      * part of the system can not be enabled by the user. This method will fail if it is called for
      * a admin that is not for the foreground user or a profile of the foreground user. Any
@@ -8647,14 +8702,18 @@
      * @param packageNames List of input method package names.
      * @return {@code true} if the operation succeeded, or {@code false} if the list didn't
      *        contain every enabled non-system input method service.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a device, profile owner or if called on
+     *                           the parent profile and the {@code admin} is not a profile owner
+     *                           of an organization-owned managed profile.
+     * @throws IllegalArgumentException if called on the parent profile, the {@code admin} is a
+     *                           profile owner of an organization-owned managed profile and the
+     *                           list of permitted input method package names is not null or empty.
      */
     public boolean setPermittedInputMethods(
             @NonNull ComponentName admin, List<String> packageNames) {
-        throwIfParentInstance("setPermittedInputMethods");
         if (mService != null) {
             try {
-                return mService.setPermittedInputMethods(admin, packageNames);
+                return mService.setPermittedInputMethods(admin, packageNames, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -8666,18 +8725,25 @@
     /**
      * Returns the list of permitted input methods set by this device or profile owner.
      * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance,
+     * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
+     * a profile owner of an organization-owned managed profile. If called on the parent instance,
+     * then the returned list of permitted input methods are those which are applied on the
+     * personal profile.
+     * <p>
      * An empty list means no input methods except system input methods are allowed. Null means all
      * input methods are allowed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return List of input method package names.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a device, profile owner or if called on
+     *                           the parent profile and the {@code admin} is not a profile owner
+     *                           of an organization-owned managed profile.
      */
     public @Nullable List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
-        throwIfParentInstance("getPermittedInputMethods");
         if (mService != null) {
             try {
-                return mService.getPermittedInputMethods(admin);
+                return mService.getPermittedInputMethods(admin, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -8687,6 +8753,11 @@
 
     /**
      * Called by the system to check if a specific input method is disabled by admin.
+     * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance,
+     * returned by {@link #getParentProfileInstance(ComponentName)}. If called on the parent
+     * instance, this method will check whether the given input method is permitted on
+     * the personal profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageName Input method package name that needs to be checked.
@@ -8699,7 +8770,8 @@
             @NonNull String packageName, int userHandle) {
         if (mService != null) {
             try {
-                return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle);
+                return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle,
+                        mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -10786,6 +10858,8 @@
      * <li>{@link #setCameraDisabled}</li>
      * <li>{@link #getCameraDisabled}</li>
      * <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li>
+     * <li>{@link #setPermittedInputMethods}</li>
+     * <li>{@link #getPermittedInputMethods}</li>
      * </ul>
      *
      * <p>The following methods can be called by the profile owner of a managed profile
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e855a1c..8f84bfe 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -73,7 +73,7 @@
     void setPasswordMinimumNonLetter(in ComponentName who, int length, boolean parent);
     int getPasswordMinimumNonLetter(in ComponentName who, int userHandle, boolean parent);
 
-    PasswordMetrics getPasswordMinimumMetrics(int userHandle);
+    PasswordMetrics getPasswordMinimumMetrics(int userHandle, boolean deviceWideOnly);
 
     void setPasswordHistoryLength(in ComponentName who, int length, boolean parent);
     int getPasswordHistoryLength(in ComponentName who, int userHandle, boolean parent);
@@ -90,7 +90,7 @@
     int getPasswordComplexity(boolean parent);
     void setRequiredPasswordComplexity(int passwordComplexity, boolean parent);
     int getRequiredPasswordComplexity(boolean parent);
-    int getAggregatedPasswordComplexityForUser(int userId);
+    int getAggregatedPasswordComplexityForUser(int userId, boolean deviceWideOnly);
     boolean isUsingUnifiedPassword(in ComponentName admin);
     int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
     int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
@@ -232,10 +232,10 @@
     List getPermittedAccessibilityServicesForUser(int userId);
     boolean isAccessibilityServicePermittedByAdmin(in ComponentName admin, String packageName, int userId);
 
-    boolean setPermittedInputMethods(in ComponentName admin,in List packageList);
-    List getPermittedInputMethods(in ComponentName admin);
+    boolean setPermittedInputMethods(in ComponentName admin,in List packageList, boolean parent);
+    List getPermittedInputMethods(in ComponentName admin, boolean parent);
     List getPermittedInputMethodsForCurrentUser();
-    boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId);
+    boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId, boolean parent);
 
     boolean setPermittedCrossProfileNotificationListeners(in ComponentName admin, in List<String> packageList);
     List<String> getPermittedCrossProfileNotificationListeners(in ComponentName admin);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 0fcf44d..3788257 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -174,8 +174,10 @@
     @NonNull
     private final Object mListenersLock = new Object();
 
-    @NonNull
-    private final RoleControllerManager mRoleControllerManager;
+    @GuardedBy("mRoleControllerManagerLock")
+    @Nullable
+    private RoleControllerManager mRoleControllerManager;
+    private final Object mRoleControllerManagerLock = new Object();
 
     /**
      * @hide
@@ -184,7 +186,6 @@
         mContext = context;
         mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
                 Context.ROLE_SERVICE));
-        mRoleControllerManager = new RoleControllerManager(context);
     }
 
     /**
@@ -693,7 +694,7 @@
     @TestApi
     public void isRoleVisible(@NonNull String roleName,
             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
-        mRoleControllerManager.isRoleVisible(roleName, executor, callback);
+        getRoleControllerManager().isRoleVisible(roleName, executor, callback);
     }
 
     /**
@@ -714,10 +715,20 @@
     @TestApi
     public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
-        mRoleControllerManager.isApplicationVisibleForRole(roleName, packageName, executor,
+        getRoleControllerManager().isApplicationVisibleForRole(roleName, packageName, executor,
                 callback);
     }
 
+    @NonNull
+    private RoleControllerManager getRoleControllerManager() {
+        synchronized (mRoleControllerManagerLock) {
+            if (mRoleControllerManager == null) {
+                mRoleControllerManager = new RoleControllerManager(mContext);
+            }
+            return mRoleControllerManager;
+        }
+    }
+
     private static class OnRoleHoldersChangedListenerDelegate
             extends IOnRoleHoldersChangedListener.Stub {
 
diff --git a/core/java/android/app/sync-data-access.png b/core/java/android/app/sync-data-access.png
new file mode 100644
index 0000000..49603d5
--- /dev/null
+++ b/core/java/android/app/sync-data-access.png
Binary files differ
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index c4d9867..0188637 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -1008,7 +1008,9 @@
         for (int i = 0; i < size; i++) {
             final Item item = mItems.get(i);
             if (item.mIntent != null) {
-                item.mIntent.prepareToEnterProcess();
+                // We can't recursively claim that this data is from a protected
+                // component, since it may have been filled in by a malicious app
+                item.mIntent.prepareToEnterProcess(false);
             }
         }
     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7843d97..1752b48 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6681,6 +6681,25 @@
             | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
             | FLAG_GRANT_PREFIX_URI_PERMISSION;
 
+    /**
+     * Local flag indicating this instance was created by copy constructor.
+     */
+    private static final int LOCAL_FLAG_FROM_COPY = 1 << 0;
+
+    /**
+     * Local flag indicating this instance was created from a {@link Parcel}.
+     */
+    private static final int LOCAL_FLAG_FROM_PARCEL = 1 << 1;
+
+    /**
+     * Local flag indicating this instance was delivered through a protected
+     * component, such as an activity that requires a signature permission, or a
+     * protected broadcast. Note that this flag <em>cannot</em> be recursively
+     * applied to any contained instances, since a malicious app may have
+     * controlled them via {@link #fillIn(Intent, int)}.
+     */
+    private static final int LOCAL_FLAG_FROM_PROTECTED_COMPONENT = 1 << 2;
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // toUri() and parseUri() options.
@@ -6798,6 +6817,8 @@
     private String mPackage;
     private ComponentName mComponent;
     private int mFlags;
+    /** Set of in-process flags which are never parceled */
+    private int mLocalFlags;
     private ArraySet<String> mCategories;
     @UnsupportedAppUsage
     private Bundle mExtras;
@@ -6848,6 +6869,11 @@
             this.mCategories = new ArraySet<>(o.mCategories);
         }
 
+        // Inherit flags from the original, plus mark that we were
+        // created by this copy constructor
+        this.mLocalFlags = o.mLocalFlags;
+        this.mLocalFlags |= LOCAL_FLAG_FROM_COPY;
+
         if (copyMode != COPY_MODE_FILTER) {
             this.mFlags = o.mFlags;
             this.mContentUserHint = o.mContentUserHint;
@@ -10931,6 +10957,9 @@
 
     /** @hide */
     protected Intent(Parcel in) {
+        // Remember that we came from a remote process to help detect security
+        // issues caused by later unsafe launches
+        mLocalFlags = LOCAL_FLAG_FROM_PARCEL;
         readFromParcel(in);
     }
 
@@ -11242,18 +11271,27 @@
                 mData = Uri.fromFile(after);
             }
         }
+
+        // Detect cases where we're about to launch a potentially unsafe intent
+        if ((mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0
+                && (mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0
+                && StrictMode.vmUnsafeIntentLaunchEnabled()) {
+            StrictMode.onUnsafeIntentLaunch(this);
+        }
     }
 
     /**
      * @hide
      */
-    public void prepareToEnterProcess() {
+    public void prepareToEnterProcess(boolean fromProtectedComponent) {
         // We just entered destination process, so we should be able to read all
         // parcelables inside.
         setDefusable(true);
 
         if (mSelector != null) {
-            mSelector.prepareToEnterProcess();
+            // We can't recursively claim that this data is from a protected
+            // component, since it may have been filled in by a malicious app
+            mSelector.prepareToEnterProcess(false);
         }
         if (mClipData != null) {
             mClipData.prepareToEnterProcess();
@@ -11265,6 +11303,10 @@
                 mContentUserHint = UserHandle.USER_CURRENT;
             }
         }
+
+        if (fromProtectedComponent) {
+            mLocalFlags |= LOCAL_FLAG_FROM_PROTECTED_COMPONENT;
+        }
     }
 
     /** @hide */
diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java
index 045c55f..d70ac91 100644
--- a/core/java/android/content/pm/AppSearchPerson.java
+++ b/core/java/android/content/pm/AppSearchPerson.java
@@ -47,32 +47,24 @@
     }
 
     public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_NAME)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_NAME)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_KEY)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_KEY)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_BOT)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
+            ).addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder(KEY_IS_BOT)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_IMPORTANT)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
+            ).addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder(KEY_IS_IMPORTANT)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
             ).build();
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 14b8df8..85549d8 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -73,131 +73,100 @@
     public static final String KEY_DISABLED_REASON = "disabledReason";
 
     public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PACKAGE_NAME)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_PACKAGE_NAME)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ACTIVITY)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ACTIVITY)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TITLE)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_TITLE)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TEXT)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_TEXT)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_MESSAGE)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_DISABLED_MESSAGE)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CATEGORIES)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_CATEGORIES)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENTS)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_INTENTS)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENT_PERSISTABLE_EXTRAS)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+            ).addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                    KEY_INTENT_PERSISTABLE_EXTRAS)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PERSON)
+            ).addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(KEY_PERSON)
                     .setSchemaType(AppSearchPerson.SCHEMA_TYPE)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_LOCUS_ID)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_LOCUS_ID)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_RANK)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+            ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_RANK)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_EXTRAS)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+            ).addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(KEY_EXTRAS)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FLAGS)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+            ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_FLAGS)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_ID)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+            ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_ICON_RES_ID)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_NAME)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ICON_RES_NAME)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_URI)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ICON_URI)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BITMAP_PATH)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+            ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_BITMAP_PATH)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_REASON)
-                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+            ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_DISABLED_REASON)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
                     .build()
 
             ).build();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 03d4d5e..747f8dc 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3532,6 +3532,17 @@
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration";
 
+    /**
+     * Feature for {@link android.view.WindowManager.LayoutParams.backgroundBlurRedius} and
+     * {@link android.graphics.drawable.BackgroundBlurDrawable}: the device supports cross-layer
+     * blurring.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
+
     /** @hide */
     public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
 
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index a54e88f..dbcd79d 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -41,4 +41,7 @@
 
     void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable);
     // =============== End of transactions used on native side as well ============================
+
+    void suppressIndividualSensorPrivacyReminders(int userId, String packageName, IBinder token,
+            boolean suppress);
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index e165ad6..f4f9e17 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -44,6 +45,18 @@
 @TestApi
 @SystemService(Context.SENSOR_PRIVACY_SERVICE)
 public final class SensorPrivacyManager {
+    /**
+     * Unique Id of this manager to identify to the service
+     * @hide
+     */
+    private IBinder token = new Binder();
+
+    /**
+     * An extra containing a sensor
+     * @hide
+     */
+    public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName()
+            + ".extra.sensor";
 
     /** Microphone
      * @hide */
@@ -299,4 +312,22 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Don't show dialogs to turn off sensor privacy for this package.
+     *
+     * @param packageName Package name not to show dialogs for
+     * @param suppress Whether to suppress or re-enable.
+     *
+     * @hide
+     */
+    public void suppressIndividualSensorPrivacyReminders(@NonNull String packageName,
+            boolean suppress) {
+        try {
+            mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName,
+                    token, suppress);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 582570e..d932865 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -89,6 +89,8 @@
     private static final int MSG_REMOVED = 105;
     private static final int MSG_CHALLENGE_GENERATED = 106;
     private static final int MSG_FINGERPRINT_DETECTED = 107;
+    private static final int MSG_UDFPS_POINTER_DOWN = 108;
+    private static final int MSG_UDFPS_POINTER_UP = 109;
 
     /**
      * Request authentication with any single sensor.
@@ -338,6 +340,20 @@
          */
         @Override
         public void onAuthenticationAcquired(int acquireInfo) {}
+
+        /**
+         * Invoked for under-display fingerprint sensors when a touch has been detected on the
+         * sensor area.
+         * @hide
+         */
+        public void onUdfpsPointerDown(int sensorId) {}
+
+        /**
+         * Invoked for under-display fingerprint sensors when a touch has been removed from the
+         * sensor area.
+         * @hide
+         */
+        public void onUdfpsPointerUp(int sensorId) {}
     }
 
     /**
@@ -1005,6 +1021,12 @@
                     sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
                             (boolean) msg.obj /* isStrongBiometric */);
                     break;
+                case MSG_UDFPS_POINTER_DOWN:
+                    sendUdfpsPointerDown(msg.arg1 /* sensorId */);
+                    break;
+                case MSG_UDFPS_POINTER_UP:
+                    sendUdfpsPointerUp(msg.arg1 /* sensorId */);
+                    break;
                 default:
                     Slog.w(TAG, "Unknown message: " + msg.what);
 
@@ -1103,6 +1125,22 @@
         mFingerprintDetectionCallback.onFingerprintDetected(sensorId, userId, isStrongBiometric);
     }
 
+    private void sendUdfpsPointerDown(int sensorId) {
+        if (mAuthenticationCallback == null) {
+            Slog.e(TAG, "sendUdfpsPointerDown, callback null");
+            return;
+        }
+        mAuthenticationCallback.onUdfpsPointerDown(sensorId);
+    }
+
+    private void sendUdfpsPointerUp(int sensorId) {
+        if (mAuthenticationCallback == null) {
+            Slog.e(TAG, "sendUdfpsPointerUp, callback null");
+            return;
+        }
+        mAuthenticationCallback.onUdfpsPointerUp(sensorId);
+    }
+
     /**
      * @hide
      */
@@ -1280,6 +1318,17 @@
             mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
                     .sendToTarget();
         }
+
+        @Override // binder call
+        public void onUdfpsPointerDown(int sensorId) {
+            mHandler.obtainMessage(MSG_UDFPS_POINTER_DOWN, sensorId, 0).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onUdfpsPointerUp(int sensorId) {
+            mHandler.obtainMessage(MSG_UDFPS_POINTER_UP, sensorId, 0).sendToTarget();
+
+        }
     };
 
 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 095b8e9..1bd284d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -30,4 +30,6 @@
     void onError(int error, int vendorCode);
     void onRemoved(in Fingerprint fp, int remaining);
     void onChallengeGenerated(int sensorId, long challenge);
+    void onUdfpsPointerDown(int sensorId);
+    void onUdfpsPointerUp(int sensorId);
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b09eda4..7858499 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -454,6 +454,33 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SystemAudioModeMuting {}
 
+    // -- Whether the HDMI CEC volume control is enabled or disabled.
+    /**
+     * HDMI CEC enabled.
+     *
+     * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
+     * @hide
+     */
+    public static final int VOLUME_CONTROL_ENABLED = 1;
+    /**
+     * HDMI CEC disabled.
+     *
+     * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
+     * @hide
+     */
+    public static final int VOLUME_CONTROL_DISABLED = 0;
+    /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
+     * @hide
+     */
+    @IntDef({
+            VOLUME_CONTROL_ENABLED,
+            VOLUME_CONTROL_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VolumeControl {}
+
+
     // -- Settings available in the CEC Configuration.
     /**
      * Name of a setting deciding whether the CEC is enabled.
@@ -492,6 +519,43 @@
     @SystemApi
     public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING =
             "system_audio_mode_muting";
+
+    /**
+     * Controls whether volume control commands via HDMI CEC are enabled.
+     *
+     * <p>Effects on different device types:
+     * <table>
+     *     <tr><th>HDMI CEC device type</th><th>0: disabled</th><th>1: enabled</th></tr>
+     *     <tr>
+     *         <td>TV (type: 0)</td>
+     *         <td>Per CEC specification.</td>
+     *         <td>TV changes system volume. TV no longer reacts to incoming volume changes
+     *         via {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio
+     *         Status>}.</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Playback device (type: 4)</td>
+     *         <td>Device sends volume commands to TV/Audio system via {@code <User Control
+     *         Pressed>}</td>
+     *         <td>Device does not send volume commands via {@code <User Control Pressed>}.</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Audio device (type: 5)</td>
+     *         <td>Full "System Audio Control" capabilities.</td>
+     *         <td>Audio device no longer reacts to incoming {@code <User Control Pressed>}
+     *         volume commands. Audio device no longer reports volume changes via {@code
+     *         <Report Audio Status>}.</td>
+     *     </tr>
+     * </table>
+     *
+     * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
+     *
+     * @hide
+     * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(int)
+     */
+    public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE =
+            "volume_control_enabled";
+
     /**
      * @hide
      */
@@ -501,6 +565,7 @@
         CEC_SETTING_NAME_POWER_CONTROL_MODE,
         CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
         CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+        CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
     })
     public @interface CecSettingName {}
 
@@ -913,14 +978,16 @@
      *
      * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
      *
-     * @param isHdmiCecVolumeControlEnabled target state of HDMI CEC volume control.
-     * @see Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED
+     * @param hdmiCecVolumeControlEnabled target state of HDMI CEC volume control.
+     * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
-    public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
+    public void setHdmiCecVolumeControlEnabled(
+            @VolumeControl int hdmiCecVolumeControlEnabled) {
         try {
-            mService.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled);
+            mService.setCecSettingIntValue(CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    hdmiCecVolumeControlEnabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -931,9 +998,10 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
-    public boolean isHdmiCecVolumeControlEnabled() {
+    @VolumeControl
+    public int getHdmiCecVolumeControlEnabled() {
         try {
-            return mService.isHdmiCecVolumeControlEnabled();
+            return mService.getCecSettingIntValue(CEC_SETTING_NAME_VOLUME_CONTROL_MODE);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1042,7 +1110,8 @@
          *
          * Note: Value of isCecAvailable is only valid when isCecEnabled is true.
          **/
-        void onStatusChange(boolean isCecEnabled, boolean isCecAvailable);
+        void onStatusChange(@HdmiControlManager.HdmiCecControl int isCecEnabled,
+                boolean isCecAvailable);
     }
 
     private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener>
@@ -1056,10 +1125,10 @@
         /**
          * Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
          *
-         * @param enabled status of HDMI CEC volume control feature
-         * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
+         * @param hdmiCecVolumeControl status of HDMI CEC volume control feature
+         * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(int)} ()}
          **/
-        void onHdmiCecVolumeControlFeature(boolean enabled);
+        void onHdmiCecVolumeControlFeature(@VolumeControl int hdmiCecVolumeControl);
     }
 
     private final ArrayMap<HdmiCecVolumeControlFeatureListener,
@@ -1283,7 +1352,7 @@
             Executor executor, final HdmiControlStatusChangeListener listener) {
         return new IHdmiControlStatusChangeListener.Stub() {
             @Override
-            public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
+            public void onStatusChange(@HdmiCecControl int isCecEnabled, boolean isCecAvailable) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable));
@@ -1360,7 +1429,7 @@
             Executor executor, final HdmiCecVolumeControlFeatureListener listener) {
         return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() {
             @Override
-            public void onHdmiCecVolumeControlFeature(boolean enabled) {
+            public void onHdmiCecVolumeControlFeature(int enabled) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled));
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 89a7afa8..9a9e945 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -273,17 +273,6 @@
         }
 
         @Override
-        public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
-            HdmiControlServiceWrapper.this.setHdmiCecVolumeControlEnabled(
-                    isHdmiCecVolumeControlEnabled);
-        }
-
-        @Override
-        public boolean isHdmiCecVolumeControlEnabled() {
-            return HdmiControlServiceWrapper.this.isHdmiCecVolumeControlEnabled();
-        }
-
-        @Override
         public void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute) {
             HdmiControlServiceWrapper.this.reportAudioStatus(deviceType, volume, maxVolume, isMute);
         }
diff --git a/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
index 873438b..f7c5887 100644
--- a/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
@@ -26,7 +26,7 @@
      * Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
      *
      * @param enabled status of HDMI CEC volume control feature
-     * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
+     * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(int)} ()}
      **/
-    void onHdmiCecVolumeControlFeature(boolean enabled);
+    void onHdmiCecVolumeControlFeature(int hdmiCecVolumeControl);
 }
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index d7329e0..7f0e53e 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -86,8 +86,6 @@
     void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data);
     void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
     void setStandbyMode(boolean isStandbyModeOn);
-    void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled);
-    boolean isHdmiCecVolumeControlEnabled();
     void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
     void setSystemAudioModeOnForAudioOnlySource();
     void addCecSettingChangeListener(String name, IHdmiCecSettingChangeListener listener);
diff --git a/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl b/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl
index 889d3fe..d61ab6a 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl
@@ -28,11 +28,11 @@
      * Called when HDMI Control (CEC) is enabled/disabled.
      *
      * @param isCecEnabled status of HDMI Control
-     * {@link android.provider.Settings.Global#HDMI_CONTROL_ENABLED}: {@code true} if enabled.
+     * {@link android.hardware.hdmi.HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_ENABLED}: {@link android.hardware.hdmi.HdmiControlManager#HDMI_CEC_CONTROL_ENABLED} if enabled.
      * @param isCecAvailable status of CEC support of the connected display (the TV).
      * {@code true} if supported.
      *
-     * Note: Value of isCecAvailable is only valid when isCecEnabled is true.
+     * Note: Value of isCecAvailable is only valid when isCecEnabled is {@link android.hardware.hdmi.HdmiControlManager#HDMI_CEC_CONTROL_ENABLED}.
      **/
-    void onStatusChange(boolean isCecEnabled, boolean isCecAvailable);
+    void onStatusChange(int isCecEnabled, boolean isCecAvailable);
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index df9a7c2..44a2e97 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -410,6 +410,11 @@
     private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
     private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
 
+    /**
+     * Timeout after which hidden IME surface will be removed from memory
+     */
+    private static final long TIMEOUT_SURFACE_REMOVAL_MILLIS = 5000;
+
     InputMethodManager mImm;
     private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
 
@@ -506,6 +511,8 @@
 
     private boolean mAutomotiveHideNavBarForKeyboard;
     private boolean mIsAutomotive;
+    private Handler mHandler;
+    private boolean mImeSurfaceScheduledForRemoval;
 
     /**
      * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
@@ -903,11 +910,31 @@
         requestHideSelf(0);
     }
 
+    private void scheduleImeSurfaceRemoval() {
+        if (mShowInputRequested || mWindowVisible || mWindow == null
+                || mImeSurfaceScheduledForRemoval) {
+            return;
+        }
+        if (mHandler == null) {
+            mHandler = new Handler(getMainLooper());
+        }
+        mImeSurfaceScheduledForRemoval = true;
+        mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS);
+    }
+
     private void removeImeSurface() {
-        if (!mShowInputRequested && !mWindowVisible) {
-            // hiding a window removes its surface.
+        // hiding a window removes its surface.
+        if (mWindow != null) {
             mWindow.hide();
         }
+        mImeSurfaceScheduledForRemoval = false;
+    }
+
+    private void cancelImeSurfaceRemoval() {
+        if (mHandler != null && mImeSurfaceScheduledForRemoval) {
+            mHandler.removeCallbacksAndMessages(null /* token */);
+            mImeSurfaceScheduledForRemoval = false;
+        }
     }
 
     private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
@@ -1043,7 +1070,7 @@
          * @hide
          */
         public final void removeImeSurface() {
-            InputMethodService.this.removeImeSurface();
+            InputMethodService.this.scheduleImeSurfaceRemoval();
         }
     }
     
@@ -2271,6 +2298,9 @@
         ImeTracing.getInstance().triggerServiceDump(
                 "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this,
                 null /* icProto */);
+        if (setVisible) {
+            cancelImeSurfaceRemoval();
+        }
         mPrivOps.applyImeVisibility(setVisible
                 ? mCurShowInputToken : mCurHideInputToken, setVisible);
     }
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index ab12cdd..3d79f28 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -39,7 +39,11 @@
  * An event logged when there is a change or event that requires updating the
  * the APF program in place with a new APF program.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class ApfProgramEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index fcafb7e..a32d3a6 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -27,7 +27,11 @@
 /**
  * An event logged for an interface with APF capabilities when its IpClient state machine exits.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class ApfStats implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 8de427d..e175d58 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -28,7 +28,11 @@
 /**
  * An event recorded when a DhcpClient state machine transitions to a new state.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class DhcpClientEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java
index de3129d..7dd0696 100644
--- a/core/java/android/net/metrics/DhcpErrorEvent.java
+++ b/core/java/android/net/metrics/DhcpErrorEvent.java
@@ -27,7 +27,11 @@
 /**
  * Event class used to record error events when parsing DHCP response packets.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class DhcpErrorEvent implements IpConnectivityLog.Event {
     public static final int L2_ERROR   = 1;
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index bb91f89..5cadb45 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -35,7 +35,11 @@
 /**
  * Class for logging IpConnectvity events with IpConnectivityMetrics
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public class IpConnectivityLog {
     private static final String TAG = IpConnectivityLog.class.getSimpleName();
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index 4f7f326..3abcc05 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -33,7 +33,11 @@
  * An event recorded by IpClient when IP provisioning completes for a network or
  * when a network disconnects.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class IpManagerEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index d5003ba..0b65bbd 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -29,7 +29,11 @@
  * An event recorded when IpReachabilityMonitor sends a neighbor probe or receives
  * a neighbor probe result.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class IpReachabilityEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 8c28f7a..47eeeff 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -31,7 +31,11 @@
 
 /**
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class NetworkEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
index b54874f..05a47e5 100644
--- a/core/java/android/net/metrics/RaEvent.java
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -25,7 +25,11 @@
 /**
  * An event logged when the APF packet socket receives an RA packet.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class RaEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 7f4e4a7..8118fe0 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -32,7 +32,11 @@
 /**
  * An event recorded by NetworkMonitor when sending a probe for finding captive portals.
  * {@hide}
+ * @deprecated The event may not be sent in Android S and above. The events
+ * are logged by a single caller in the system using signature permissions
+ * and that caller is migrating to statsd.
  */
+@Deprecated
 @SystemApi
 public final class ValidationProbeEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 0b2cfdd..bc3d5c4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -72,4 +72,7 @@
     boolean deviceSupportsNfcSecure();
     boolean setNfcSecure(boolean enable);
 
+    boolean setAlwaysOn(boolean value);
+    boolean isAlwaysOnEnabled();
+    boolean isAlwaysOnSupported();
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index a17a5370..e85eb93 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -350,6 +350,22 @@
             "android.nfc.extra.HANDOVER_TRANSFER_STATUS";
 
     /** @hide */
+    public static final String ACTION_ALWAYS_ON_STATE_CHANGED =
+            "android.nfc.action.ALWAYS_ON_STATE_CHANGED";
+
+    /**
+     * Used as an int extra field in {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
+     * intents to request the current power state. Possible values are:
+     * {@link #STATE_OFF},
+     * {@link #STATE_TURNING_ON},
+     * {@link #STATE_ON},
+     * {@link #STATE_TURNING_OFF},
+     * @hide
+     */
+    public static final String EXTRA_ALWAYS_ON_STATE =
+            "android.nfc.extra.ALWAYS_ON_STATE";
+
+    /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
     /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
@@ -358,6 +374,14 @@
     public static final String EXTRA_HANDOVER_TRANSFER_URI =
             "android.nfc.extra.HANDOVER_TRANSFER_URI";
 
+    /**
+     * Broadcast Action: Notify possible NFC transaction blocked because device is locked.
+     * <p>An external NFC field detected when device locked and SecureNfc enabled.
+     * @hide
+     */
+    public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC =
+            "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
+
     // Guarded by NfcAdapter.class
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
@@ -2211,4 +2235,106 @@
             return mContext.getApplicationInfo().targetSdkVersion;
         }
     }
+
+    /**
+     * Sets NFC controller always on feature.
+     * <p>This API is for the NFCC internal state management. It allows to discriminate
+     * the controller function from the NFC function by keeping the NFC Controller on without
+     * any NFC RF enabled if necessary.
+     * <p>This call is asynchronous. Listen for {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
+     * broadcasts to find out when the operation is complete.
+     * <p>If this returns true, then either NFCC is already on, or
+     * a {@link #ACTION_ALWAYS_ON_STATE_CHANGED} broadcast will be sent to indicate
+     * a state transition.
+     * If this returns false, then there is some problem that prevents an attempt to turn NFCC on.
+     * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
+     * disabled), if false the NFCC will follow completely the Nfc adapter state.
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @return void
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean setAlwaysOn(boolean value) {
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.setAlwaysOn(value);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.setAlwaysOn(value);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks NFC controller always on feature is enabled.
+     *
+     * @return True if NFC controller always on is enabled, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @hide
+     */
+
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean isAlwaysOnEnabled() {
+        try {
+            return sService.isAlwaysOnEnabled();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isAlwaysOnEnabled();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks if the device supports NFC controller always on functionality.
+     *
+     * @return True if device supports NFC controller always on, false otherwise
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @hide
+     */
+
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean isAlwaysOnSupported() {
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.isAlwaysOnSupported();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            // Try one more time
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+                return false;
+            }
+            try {
+                return sService.isAlwaysOnSupported();
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover NFC Service.");
+            }
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java
index fdccaae..2256365 100644
--- a/core/java/android/nfc/tech/Ndef.java
+++ b/core/java/android/nfc/tech/Ndef.java
@@ -112,7 +112,7 @@
     public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1";
     /** NFC Forum Tag Type 2 */
     public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2";
-    /** NFC Forum Tag Type 4 */
+    /** NFC Forum Tag Type 3 */
     public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3";
     /** NFC Forum Tag Type 4 */
     public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4";
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index b13be9f..1189fdb 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -52,6 +52,7 @@
 import android.os.strictmode.ServiceConnectionLeakedViolation;
 import android.os.strictmode.SqliteObjectLeakedViolation;
 import android.os.strictmode.UnbufferedIoViolation;
+import android.os.strictmode.UnsafeIntentLaunchViolation;
 import android.os.strictmode.UntaggedSocketViolation;
 import android.os.strictmode.Violation;
 import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation;
@@ -256,6 +257,7 @@
             DETECT_VM_NON_SDK_API_USAGE,
             DETECT_VM_IMPLICIT_DIRECT_BOOT,
             DETECT_VM_INCORRECT_CONTEXT_USE,
+            DETECT_VM_UNSAFE_INTENT_LAUNCH,
             PENALTY_GATHER,
             PENALTY_LOG,
             PENALTY_DIALOG,
@@ -297,6 +299,8 @@
     private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11;
     /** @hide */
     private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
+    /** @hide */
+    private static final int DETECT_VM_UNSAFE_INTENT_LAUNCH = 1 << 13;
 
     /** @hide */
     private static final int DETECT_VM_ALL = 0x0000ffff;
@@ -854,6 +858,7 @@
              * <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
              * other closable objects but will likely expand in future releases.
              */
+            @SuppressWarnings("AndroidFrameworkCompatChange")
             public @NonNull Builder detectAll() {
                 detectLeakedSqlLiteObjects();
 
@@ -885,6 +890,9 @@
                 if (targetSdk >= Build.VERSION_CODES.R) {
                     detectIncorrectContextUse();
                 }
+                if (targetSdk >= Build.VERSION_CODES.S) {
+                    detectUnsafeIntentLaunch();
+                }
 
                 // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
                 // TODO: enable detectImplicitDirectBoot() once system is less noisy
@@ -1067,6 +1075,59 @@
             }
 
             /**
+             * Detect when your app launches an {@link Intent} which originated
+             * from outside your app.
+             * <p>
+             * Violations may indicate security vulnerabilities in the design of
+             * your app, where a malicious app could trick you into granting
+             * {@link Uri} permissions or launching unexported components. Here
+             * are some typical design patterns that can be used to safely
+             * resolve these violations:
+             * <ul>
+             * <li>The ideal approach is to migrate to using a
+             * {@link android.app.PendingIntent}, which ensures that your launch is
+             * performed using the identity of the original creator, completely
+             * avoiding the security issues described above.
+             * <li>If using a {@link android.app.PendingIntent} isn't feasible, an
+             * alternative approach is to create a brand new {@link Intent} and
+             * carefully copy only specific values from the original
+             * {@link Intent} after careful validation.
+             * </ul>
+             * <p>
+             * Note that this <em>may</em> detect false-positives if your app
+             * sends itself an {@link Intent} which is first routed through the
+             * OS, such as using {@link Intent#createChooser}. In these cases,
+             * careful inspection is required to determine if the return point
+             * into your app is appropriately protected with a signature
+             * permission or marked as unexported. If the return point is not
+             * protected, your app is likely vulnerable to malicious apps.
+             *
+             * @see Context#startActivity(Intent)
+             * @see Context#startService(Intent)
+             * @see Context#bindService(Intent, ServiceConnection, int)
+             * @see Context#sendBroadcast(Intent)
+             * @see android.app.Activity#setResult(int, Intent)
+             */
+            public @NonNull Builder detectUnsafeIntentLaunch() {
+                return enable(DETECT_VM_UNSAFE_INTENT_LAUNCH);
+            }
+
+            /**
+             * Permit your app to launch any {@link Intent} which originated
+             * from outside your app.
+             * <p>
+             * Disabling this check is <em>strongly discouraged</em>, as
+             * violations may indicate security vulnerabilities in the design of
+             * your app, where a malicious app could trick you into granting
+             * {@link Uri} permissions or launching unexported components.
+             *
+             * @see #detectUnsafeIntentLaunch()
+             */
+            public @NonNull Builder permitUnsafeIntentLaunch() {
+                return disable(DETECT_VM_UNSAFE_INTENT_LAUNCH);
+            }
+
+            /**
              * Crashes the whole process on violation. This penalty runs at the end of all enabled
              * penalties so you'll still get your logging or other violations before the process
              * dies.
@@ -2115,6 +2176,11 @@
     }
 
     /** @hide */
+    public static boolean vmUnsafeIntentLaunchEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_UNSAFE_INTENT_LAUNCH) != 0;
+    }
+
+    /** @hide */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
     }
@@ -2221,6 +2287,11 @@
         }
     }
 
+    /** @hide */
+    public static void onUnsafeIntentLaunch(Intent intent) {
+        onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent));
+    }
+
     /** Assume locked until we hear otherwise */
     private static volatile boolean sUserKeyUnlocked = false;
 
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 0330500..30afe38 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -27,6 +28,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -38,6 +41,14 @@
 public class SystemVibrator extends Vibrator {
     private static final String TAG = "Vibrator";
 
+    private static final int VIBRATOR_PRESENT_UNKNOWN = 0;
+    private static final int VIBRATOR_PRESENT_YES = 1;
+    private static final int VIBRATOR_PRESENT_NO = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({VIBRATOR_PRESENT_UNKNOWN, VIBRATOR_PRESENT_YES, VIBRATOR_PRESENT_NO})
+    private @interface VibratorPresent {}
+
     private final IVibratorService mService;
     private final IVibratorManagerService mManagerService;
     private final Object mLock = new Object();
@@ -45,6 +56,9 @@
     private final Context mContext;
     @GuardedBy("mLock")
     private VibratorInfo mVibratorInfo;
+    @GuardedBy("mLock")
+    @VibratorPresent
+    private int mVibratorPresent;
 
     @GuardedBy("mDelegates")
     private final ArrayMap<OnVibratorStateChangedListener,
@@ -69,15 +83,18 @@
 
     @Override
     public boolean hasVibrator() {
-        if (mService == null) {
-            Log.w(TAG, "Failed to vibrate; no vibrator service.");
+        try {
+            synchronized (mLock) {
+                if (mVibratorPresent == VIBRATOR_PRESENT_UNKNOWN && mService != null) {
+                    mVibratorPresent =
+                            mService.hasVibrator() ? VIBRATOR_PRESENT_YES : VIBRATOR_PRESENT_NO;
+                }
+                return mVibratorPresent == VIBRATOR_PRESENT_YES;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to query vibrator presence", e);
             return false;
         }
-        try {
-            return mService.hasVibrator();
-        } catch (RemoteException e) {
-        }
-        return false;
     }
 
     /**
diff --git a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
new file mode 100644
index 0000000..891fb59
--- /dev/null
+++ b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.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 android.os.strictmode;
+
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Violation raised when your app launches an {@link Intent} which originated
+ * from outside your app.
+ * <p>
+ * Violations may indicate security vulnerabilities in the design of your app,
+ * where a malicious app could trick you into granting {@link Uri} permissions
+ * or launching unexported components. Here are some typical design patterns
+ * that can be used to safely resolve these violations:
+ * <ul>
+ * <li>The ideal approach is to migrate to using a {@link PendingIntent}, which
+ * ensures that your launch is performed using the identity of the original
+ * creator, completely avoiding the security issues described above.
+ * <li>If using a {@link PendingIntent} isn't feasible, an alternative approach
+ * is to create a brand new {@link Intent} and carefully copy only specific
+ * values from the original {@link Intent} after careful validation.
+ * </ul>
+ * <p>
+ * Note that this <em>may</em> detect false-positives if your app sends itself
+ * an {@link Intent} which is first routed through the OS, such as using
+ * {@link Intent#createChooser}. In these cases, careful inspection is required
+ * to determine if the return point into your app is appropriately protected
+ * with a signature permission or marked as unexported. If the return point is
+ * not protected, your app is likely vulnerable to malicious apps.
+ */
+public final class UnsafeIntentLaunchViolation extends Violation {
+    /** @hide */
+    public UnsafeIntentLaunchViolation(@NonNull Intent intent) {
+        super("Launch of unsafe intent: " + intent);
+    }
+}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0bdd574..33e33ee 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentProvider;
@@ -189,7 +190,7 @@
      * This method is used by Telephony to store pictures selected by the user or sent from the
      * remote party as part of a voice call with call composer. The {@link Uri} supplied in the
      * callback can be used to retrieve the image via {@link ContentResolver#openFile} or stored in
-     * the {@link Calls} table in the TODO: link column name.
+     * the {@link Calls} table in the {@link Calls#COMPOSER_PHOTO_URI} column.
      *
      * The caller is responsible for closing the {@link InputStream} after the callback indicating
      * success or failure.
@@ -388,6 +389,320 @@
     }
 
     /**
+     * Used as an argument to {@link Calls#addCall(Context, AddCallParams)}.
+     *
+     * Contains details to log about a call.
+     * @hide
+     */
+    public static class AddCallParams {
+
+        /**
+         * Builder for the add-call parameters.
+         */
+        public static final class AddCallParametersBuilder {
+            private CallerInfo mCallerInfo;
+            private String mNumber;
+            private String mPostDialDigits;
+            private String mViaNumber;
+            private int mPresentation = TelecomManager.PRESENTATION_UNKNOWN;
+            private int mCallType = Calls.INCOMING_TYPE;
+            private int mFeatures;
+            private PhoneAccountHandle mAccountHandle;
+            private long mStart;
+            private int mDuration;
+            private Long mDataUsage = Long.MIN_VALUE;
+            private boolean mAddForAllUsers;
+            private UserHandle mUserToBeInsertedTo;
+            private boolean mIsRead;
+            private int mCallBlockReason = Calls.BLOCK_REASON_NOT_BLOCKED;
+            private CharSequence mCallScreeningAppName;
+            private String mCallScreeningComponentName;
+            private long mMissedReason = Calls.MISSED_REASON_NOT_MISSED;
+            private int mPriority = Calls.PRIORITY_NORMAL;
+            private String mSubject;
+            private double mLatitude = Double.NaN;
+            private double mLongitude = Double.NaN;
+            private Uri mPictureUri;
+
+            /**
+             * @param callerInfo the CallerInfo object to get the target contact from.
+             */
+            public @NonNull AddCallParametersBuilder setCallerInfo(
+                    @NonNull CallerInfo callerInfo) {
+                mCallerInfo = callerInfo;
+                return this;
+            }
+
+            /**
+             * @param number the phone number to be added to the calls db
+             */
+            public @NonNull AddCallParametersBuilder setNumber(@NonNull String number) {
+                mNumber = number;
+                return this;
+            }
+
+            /**
+             * @param postDialDigits the post-dial digits that were dialed after the number,
+             *        if it was outgoing. Otherwise it is ''.
+             */
+            public @NonNull AddCallParametersBuilder setPostDialDigits(
+                    @NonNull String postDialDigits) {
+                mPostDialDigits = postDialDigits;
+                return this;
+            }
+
+            /**
+             * @param viaNumber the secondary number that the incoming call received with. If the
+             *        call was received with the SIM assigned number, then this field must be ''.
+             */
+            public @NonNull AddCallParametersBuilder setViaNumber(@NonNull String viaNumber) {
+                mViaNumber = viaNumber;
+                return this;
+            }
+
+            /**
+             * @param presentation enum value from TelecomManager.PRESENTATION_xxx, which
+             *        is set by the network and denotes the number presenting rules for
+             *        "allowed", "payphone", "restricted" or "unknown"
+             */
+            public @NonNull AddCallParametersBuilder setPresentation(int presentation) {
+                mPresentation = presentation;
+                return this;
+            }
+
+            /**
+             * @param callType enumerated values for "incoming", "outgoing", or "missed"
+             */
+            public @NonNull AddCallParametersBuilder setCallType(int callType) {
+                mCallType = callType;
+                return this;
+            }
+
+            /**
+             * @param features features of the call (e.g. Video).
+             */
+            public @NonNull AddCallParametersBuilder setFeatures(int features) {
+                mFeatures = features;
+                return this;
+            }
+
+            /**
+             * @param accountHandle The accountHandle object identifying the provider of the call
+             */
+            public @NonNull AddCallParametersBuilder setAccountHandle(
+                    @NonNull PhoneAccountHandle accountHandle) {
+                mAccountHandle = accountHandle;
+                return this;
+            }
+
+            /**
+             * @param start time stamp for the call in milliseconds
+             */
+            public @NonNull AddCallParametersBuilder setStart(long start) {
+                mStart = start;
+                return this;
+            }
+
+            /**
+             * @param duration call duration in seconds
+             */
+            public @NonNull AddCallParametersBuilder setDuration(int duration) {
+                mDuration = duration;
+                return this;
+            }
+
+            /**
+             * @param dataUsage data usage for the call in bytes or
+             *                  {@link Long#MIN_VALUE} if data usage was not tracked
+             *                  for the call.
+             */
+            public @NonNull AddCallParametersBuilder setDataUsage(long dataUsage) {
+                mDataUsage = dataUsage;
+                return this;
+            }
+
+            /**
+             * @param addForAllUsers If true, the call is added to the call log of all currently
+             *        running users. The caller must have the MANAGE_USERS permission if this is
+             *        true.
+             */
+            public @NonNull AddCallParametersBuilder setAddForAllUsers(
+                    boolean addForAllUsers) {
+                mAddForAllUsers = addForAllUsers;
+                return this;
+            }
+
+            /**
+             * @param userToBeInsertedTo {@link UserHandle} of user that the call is going to be
+             *                           inserted to. null if it is inserted to the current user.
+             *                           The value is ignored if {@link #setAddForAllUsers} is
+             *                           called with {@code true}.
+             */
+            @SuppressLint("UserHandleName")
+            public @NonNull AddCallParametersBuilder setUserToBeInsertedTo(
+                    @NonNull UserHandle userToBeInsertedTo) {
+                mUserToBeInsertedTo = userToBeInsertedTo;
+                return this;
+            }
+
+            /**
+             * @param isRead Flag to show if the missed call log has been read by the user or not.
+             *                Used for call log restore of missed calls.
+             */
+            public @NonNull AddCallParametersBuilder setIsRead(boolean isRead) {
+                mIsRead = isRead;
+                return this;
+            }
+
+            /**
+             * @param callBlockReason The reason why the call is blocked.
+             */
+            public @NonNull AddCallParametersBuilder setCallBlockReason(int callBlockReason) {
+                mCallBlockReason = callBlockReason;
+                return this;
+            }
+
+            /**
+             * @param callScreeningAppName The call screening application name which block the call.
+             */
+            public @NonNull AddCallParametersBuilder setCallScreeningAppName(
+                    @NonNull CharSequence callScreeningAppName) {
+                mCallScreeningAppName = callScreeningAppName;
+                return this;
+            }
+
+            /**
+             * @param callScreeningComponentName The call screening component name which blocked
+             *                                   the call.
+             */
+            public @NonNull AddCallParametersBuilder setCallScreeningComponentName(
+                    @NonNull String callScreeningComponentName) {
+                mCallScreeningComponentName = callScreeningComponentName;
+                return this;
+            }
+
+            /**
+             * @param missedReason The encoded missed information of the call.
+             */
+            public @NonNull AddCallParametersBuilder setMissedReason(long missedReason) {
+                mMissedReason = missedReason;
+                return this;
+            }
+
+            /**
+             * @param priority The priority of the call, either {@link Calls#PRIORITY_NORMAL}
+             *                 or {@link Calls#PRIORITY_URGENT} as sent via call composer
+             */
+            public @NonNull AddCallParametersBuilder setPriority(int priority) {
+                mPriority = priority;
+                return this;
+            }
+
+            /**
+             * @param subject The subject as sent via call composer.
+             */
+            public @NonNull AddCallParametersBuilder setSubject(@NonNull String subject) {
+                mSubject = subject;
+                return this;
+            }
+
+            /**
+             * @param latitude Latitude of the location sent via call composer.
+             */
+            public @NonNull AddCallParametersBuilder setLatitude(double latitude) {
+                mLatitude = latitude;
+                return this;
+            }
+
+            /**
+             * @param longitude Longitude of the location sent via call composer.
+             */
+            public @NonNull AddCallParametersBuilder setLongitude(double longitude) {
+                mLongitude = longitude;
+                return this;
+            }
+
+            /**
+             * @param pictureUri {@link Uri} returned from {@link #storeCallComposerPictureAsUser}.
+             *                   Associates that stored picture with this call in the log.
+             */
+            public @NonNull AddCallParametersBuilder setPictureUri(@NonNull Uri pictureUri) {
+                mPictureUri = pictureUri;
+                return this;
+            }
+
+            /**
+             * Builds the object
+             */
+            public @NonNull AddCallParams build() {
+                return new AddCallParams(mCallerInfo, mNumber, mPostDialDigits, mViaNumber,
+                        mPresentation, mCallType, mFeatures, mAccountHandle, mStart, mDuration,
+                        mDataUsage, mAddForAllUsers, mUserToBeInsertedTo, mIsRead, mCallBlockReason,
+                        mCallScreeningAppName, mCallScreeningComponentName, mMissedReason,
+                        mPriority, mSubject, mLatitude, mLongitude, mPictureUri);
+            }
+        }
+
+        private CallerInfo mCallerInfo;
+        private String mNumber;
+        private String mPostDialDigits;
+        private String mViaNumber;
+        private int mPresentation;
+        private int mCallType;
+        private int mFeatures;
+        private PhoneAccountHandle mAccountHandle;
+        private long mStart;
+        private int mDuration;
+        private long mDataUsage;
+        private boolean mAddForAllUsers;
+        private UserHandle mUserToBeInsertedTo;
+        private boolean mIsRead;
+        private int mCallBlockReason;
+        private CharSequence mCallScreeningAppName;
+        private String mCallScreeningComponentName;
+        private long mMissedReason;
+        private int mPriority;
+        private String mSubject;
+        private double mLatitude = Double.NaN;
+        private double mLongitude = Double.NaN;
+        private Uri mPictureUri;
+
+        private AddCallParams(CallerInfo callerInfo, String number, String postDialDigits,
+                String viaNumber, int presentation, int callType, int features,
+                PhoneAccountHandle accountHandle, long start, int duration, long dataUsage,
+                boolean addForAllUsers, UserHandle userToBeInsertedTo, boolean isRead,
+                int callBlockReason,
+                CharSequence callScreeningAppName, String callScreeningComponentName,
+                long missedReason,
+                int priority, String subject, double latitude, double longitude, Uri pictureUri) {
+            mCallerInfo = callerInfo;
+            mNumber = number;
+            mPostDialDigits = postDialDigits;
+            mViaNumber = viaNumber;
+            mPresentation = presentation;
+            mCallType = callType;
+            mFeatures = features;
+            mAccountHandle = accountHandle;
+            mStart = start;
+            mDuration = duration;
+            mDataUsage = dataUsage;
+            mAddForAllUsers = addForAllUsers;
+            mUserToBeInsertedTo = userToBeInsertedTo;
+            mIsRead = isRead;
+            mCallBlockReason = callBlockReason;
+            mCallScreeningAppName = callScreeningAppName;
+            mCallScreeningComponentName = callScreeningComponentName;
+            mMissedReason = missedReason;
+            mPriority = priority;
+            mSubject = subject;
+            mLatitude = latitude;
+            mLongitude = longitude;
+            mPictureUri = pictureUri;
+        }
+
+    }
+
+    /**
      * Contains the recent calls.
      */
     public static class Calls implements BaseColumns {
@@ -1066,6 +1381,83 @@
         public static final String MISSED_REASON = "missed_reason";
 
         /**
+         * The subject of the call, as delivered via call composer.
+         *
+         * For outgoing calls, contains the subject set by the local user. For incoming calls,
+         * contains the subject set by the remote caller. May be null if no subject was set.
+         * <p>Type: TEXT</p>
+         */
+        public static final String SUBJECT = "subject";
+
+        /**
+         * Used as a value in the {@link #PRIORITY} column.
+         *
+         * Indicates that the call is of normal priority. This is also the default value for calls
+         * that did not include call composer elements.
+         */
+        public static final int PRIORITY_NORMAL = 0;
+
+        /**
+         * Used as a value in the {@link #PRIORITY} column.
+         *
+         * Indicates that the call is of urgent priority.
+         */
+        public static final int PRIORITY_URGENT = 1;
+
+        /**
+         * The priority of the call, as delivered via call composer.
+         *
+         * For outgoing calls, contains the priority set by the local user. For incoming calls,
+         * contains the priority set by the remote caller. If no priority was set or the call
+         * did not include call composer elements, defaults to {@link #PRIORITY_NORMAL}.
+         * Valid values are {@link #PRIORITY_NORMAL} and {@link #PRIORITY_URGENT}.
+         * <p>Type: INTEGER</p>
+         */
+        public static final String PRIORITY = "priority";
+
+        /**
+         * A reference to the picture that was sent via call composer.
+         *
+         * The string contained in this field should be converted to an {@link Uri} via
+         * {@link Uri#parse(String)}, then passed to {@link ContentResolver#openFileDescriptor}
+         * in order to obtain a file descriptor to access the picture data.
+         *
+         * The user may choose to delete the picture associated with a call independently of the
+         * call log entry, in which case {@link ContentResolver#openFileDescriptor} may throw a
+         * {@link FileNotFoundException}.
+         *
+         * Note that pictures sent or received via call composer will not be included in any
+         * backups of the call log.
+         *
+         * <p>Type: TEXT</p>
+         */
+        public static final String COMPOSER_PHOTO_URI = "composer_photo_uri";
+
+        /**
+         * A reference to the location that was sent via call composer.
+         *
+         * This column contains the content URI of the corresponding entry in {@link Locations}
+         * table, which contains the actual location data. The
+         * {@link Manifest.permission#ACCESS_FINE_LOCATION} permission is required to access that
+         * table.
+         *
+         * If your app has the appropriate permissions, the location data may be obtained by
+         * converting the value of this column to an {@link Uri} via {@link Uri#parse}, then passing
+         * the result to {@link ContentResolver#query}.
+         *
+         * The user may choose to delete the location associated with a call independently of the
+         * call log entry, in which case the {@link Cursor} returned from
+         * {@link ContentResolver#query} will either be {@code null} or empty, with
+         * {@link Cursor#getCount()} returning {@code 0}.
+         *
+         * This column will not be populated when a call is received while the device is locked, and
+         * it will not be part of any backups.
+         *
+         * <p>Type: TEXT</p>
+         */
+        public static final String LOCATION = "location";
+
+        /**
          * Adds a call to the call log.
          *
          * @param ci the CallerInfo object to get the target contact from.  Can be null
@@ -1139,6 +1531,7 @@
                 null /* callScreeningComponentName */, missedReason);
         }
 
+
         /**
          * Adds a call to the call log.
          *
@@ -1184,19 +1577,52 @@
                 Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
                 boolean isRead, int callBlockReason, CharSequence callScreeningAppName,
                 String callScreeningComponentName, long missedReason) {
+            AddCallParams.AddCallParametersBuilder builder =
+                    new AddCallParams.AddCallParametersBuilder();
+            builder.setCallerInfo(ci);
+            builder.setNumber(number);
+            builder.setPostDialDigits(postDialDigits);
+            builder.setViaNumber(viaNumber);
+            builder.setPresentation(presentation);
+            builder.setCallType(callType);
+            builder.setFeatures(features);
+            builder.setAccountHandle(accountHandle);
+            builder.setStart(start);
+            builder.setDuration(duration);
+            builder.setDataUsage(dataUsage == null ? Long.MIN_VALUE : dataUsage);
+            builder.setAddForAllUsers(addForAllUsers);
+            builder.setUserToBeInsertedTo(userToBeInsertedTo);
+            builder.setIsRead(isRead);
+            builder.setCallBlockReason(callBlockReason);
+            builder.setCallScreeningAppName(callScreeningAppName);
+            builder.setCallScreeningComponentName(callScreeningComponentName);
+            builder.setMissedReason(missedReason);
+
+            return addCall(context, builder.build());
+        }
+
+        /**
+         * Adds a call to the call log, using the provided parameters
+         * @result The URI of the call log entry belonging to the user that made or received this
+         *        call.  This could be of the shadow provider.  Do not return it to non-system apps,
+         *        as they don't have permissions.
+         * @hide
+         */
+        public static @NonNull Uri addCall(
+                @NonNull Context context, @NonNull AddCallParams params) {
             if (VERBOSE_LOG) {
                 Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
-                        number, userToBeInsertedTo, addForAllUsers));
+                        params.mNumber, params.mUserToBeInsertedTo, params.mAddForAllUsers));
             }
             final ContentResolver resolver = context.getContentResolver();
 
-            String accountAddress = getLogAccountAddress(context, accountHandle);
+            String accountAddress = getLogAccountAddress(context, params.mAccountHandle);
 
-            int numberPresentation = getLogNumberPresentation(number, presentation);
-            String name = (ci != null) ? ci.getName() : "";
+            int numberPresentation = getLogNumberPresentation(params.mNumber, params.mPresentation);
+            String name = (params.mCallerInfo != null) ? params.mCallerInfo.getName() : "";
             if (numberPresentation != PRESENTATION_ALLOWED) {
-                number = "";
-                if (ci != null) {
+                params.mNumber = "";
+                if (params.mCallerInfo != null) {
                     name = "";
                 }
             }
@@ -1204,41 +1630,46 @@
             // accountHandle information
             String accountComponentString = null;
             String accountId = null;
-            if (accountHandle != null) {
-                accountComponentString = accountHandle.getComponentName().flattenToString();
-                accountId = accountHandle.getId();
+            if (params.mAccountHandle != null) {
+                accountComponentString = params.mAccountHandle.getComponentName().flattenToString();
+                accountId = params.mAccountHandle.getId();
             }
 
-            ContentValues values = new ContentValues(6);
+            ContentValues values = new ContentValues(14);
 
-            values.put(NUMBER, number);
-            values.put(POST_DIAL_DIGITS, postDialDigits);
-            values.put(VIA_NUMBER, viaNumber);
+            values.put(NUMBER, params.mNumber);
+            values.put(POST_DIAL_DIGITS, params.mPostDialDigits);
+            values.put(VIA_NUMBER, params.mViaNumber);
             values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
-            values.put(TYPE, Integer.valueOf(callType));
-            values.put(FEATURES, features);
-            values.put(DATE, Long.valueOf(start));
-            values.put(DURATION, Long.valueOf(duration));
-            if (dataUsage != null) {
-                values.put(DATA_USAGE, dataUsage);
+            values.put(TYPE, Integer.valueOf(params.mCallType));
+            values.put(FEATURES, params.mFeatures);
+            values.put(DATE, Long.valueOf(params.mStart));
+            values.put(DURATION, Long.valueOf(params.mDuration));
+            if (params.mDataUsage != Long.MIN_VALUE) {
+                values.put(DATA_USAGE, params.mDataUsage);
             }
             values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
             values.put(PHONE_ACCOUNT_ID, accountId);
             values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
             values.put(NEW, Integer.valueOf(1));
             values.put(CACHED_NAME, name);
-            values.put(ADD_FOR_ALL_USERS, addForAllUsers ? 1 : 0);
+            values.put(ADD_FOR_ALL_USERS, params.mAddForAllUsers ? 1 : 0);
 
-            if (callType == MISSED_TYPE) {
-                values.put(IS_READ, Integer.valueOf(isRead ? 1 : 0));
+            if (params.mCallType == MISSED_TYPE) {
+                values.put(IS_READ, Integer.valueOf(params.mIsRead ? 1 : 0));
             }
 
-            values.put(BLOCK_REASON, callBlockReason);
-            values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName));
-            values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
-            values.put(MISSED_REASON, Long.valueOf(missedReason));
+            values.put(BLOCK_REASON, params.mCallBlockReason);
+            values.put(CALL_SCREENING_APP_NAME, charSequenceToString(params.mCallScreeningAppName));
+            values.put(CALL_SCREENING_COMPONENT_NAME, params.mCallScreeningComponentName);
+            values.put(MISSED_REASON, Long.valueOf(params.mMissedReason));
+            values.put(PRIORITY, params.mPriority);
+            values.put(SUBJECT, params.mSubject);
+            if (params.mPictureUri != null) {
+                values.put(COMPOSER_PHOTO_URI, params.mPictureUri.toString());
+            }
 
-            if ((ci != null) && (ci.getContactId() > 0)) {
+            if ((params.mCallerInfo != null) && (params.mCallerInfo.getContactId() > 0)) {
                 // Update usage information for the number associated with the contact ID.
                 // We need to use both the number and the ID for obtaining a data ID since other
                 // contacts may have the same number.
@@ -1247,23 +1678,23 @@
 
                 // We should prefer normalized one (probably coming from
                 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
-                if (ci.normalizedNumber != null) {
-                    final String normalizedPhoneNumber = ci.normalizedNumber;
+                if (params.mCallerInfo.normalizedNumber != null) {
+                    final String normalizedPhoneNumber = params.mCallerInfo.normalizedNumber;
                     cursor = resolver.query(Phone.CONTENT_URI,
                             new String[] { Phone._ID },
                             Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
-                            new String[] { String.valueOf(ci.getContactId()),
+                            new String[] { String.valueOf(params.mCallerInfo.getContactId()),
                                     normalizedPhoneNumber},
                             null);
                 } else {
-                    final String phoneNumber = ci.getPhoneNumber() != null
-                        ? ci.getPhoneNumber() : number;
+                    final String phoneNumber = params.mCallerInfo.getPhoneNumber() != null
+                            ? params.mCallerInfo.getPhoneNumber() : params.mNumber;
                     cursor = resolver.query(
                             Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
                                     Uri.encode(phoneNumber)),
                             new String[] { Phone._ID },
                             Phone.CONTACT_ID + " =?",
-                            new String[] { String.valueOf(ci.getContactId()) },
+                            new String[] { String.valueOf(params.mCallerInfo.getContactId()) },
                             null);
                 }
 
@@ -1272,10 +1703,10 @@
                         if (cursor.getCount() > 0 && cursor.moveToFirst()) {
                             final String dataId = cursor.getString(0);
                             updateDataUsageStatForData(resolver, dataId);
-                            if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS
-                                    && callType == Calls.OUTGOING_TYPE
-                                    && TextUtils.isEmpty(ci.normalizedNumber)) {
-                                updateNormalizedNumber(context, resolver, dataId, number);
+                            if (params.mDuration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS
+                                    && params.mCallType == Calls.OUTGOING_TYPE
+                                    && TextUtils.isEmpty(params.mCallerInfo.normalizedNumber)) {
+                                updateNormalizedNumber(context, resolver, dataId, params.mNumber);
                             }
                         }
                     } finally {
@@ -1311,7 +1742,18 @@
             final UserManager userManager = context.getSystemService(UserManager.class);
             final int currentUserId = userManager.getUserHandle();
 
-            if (addForAllUsers) {
+            if (params.mAddForAllUsers) {
+                if (userManager.isUserUnlocked(UserHandle.SYSTEM)) {
+                    // If the user is unlocked, insert to the location provider if a location is
+                    // provided. Do not store location if the device is still locked -- this
+                    // puts it into device-encrypted storage instead of credential-encrypted
+                    // storage.
+                    Uri locationUri = maybeInsertLocation(params, resolver, UserHandle.SYSTEM);
+                    if (locationUri != null) {
+                        values.put(Calls.LOCATION, locationUri.toString());
+                    }
+                }
+
                 // First, insert to the system user.
                 final Uri uriForSystem = addEntryAndRemoveExpiredEntries(
                         context, userManager, UserHandle.SYSTEM, values);
@@ -1351,6 +1793,12 @@
                     // start.
                     if (userManager.isUserRunning(userHandle)
                             && userManager.isUserUnlocked(userHandle)) {
+                        Uri locationUri = maybeInsertLocation(params, resolver, userHandle);
+                        if (locationUri != null) {
+                            values.put(Calls.LOCATION, locationUri.toString());
+                        } else {
+                            values.put(Calls.LOCATION, (String) null);
+                        }
                         final Uri uri = addEntryAndRemoveExpiredEntries(context, userManager,
                                 userHandle, values);
                         if (userId == currentUserId) {
@@ -1361,10 +1809,20 @@
             } else {
                 // Single-user entry. Just write to that user, assuming it's running.  If the
                 // user is encrypted, we write to the shadow calllog.
-
-                final UserHandle targetUserHandle = userToBeInsertedTo != null
-                        ? userToBeInsertedTo
+                final UserHandle targetUserHandle = params.mUserToBeInsertedTo != null
+                        ? params.mUserToBeInsertedTo
                         : UserHandle.of(currentUserId);
+
+                if (userManager.isUserRunning(targetUserHandle)
+                        && userManager.isUserUnlocked(targetUserHandle)) {
+                    Uri locationUri = maybeInsertLocation(params, resolver, targetUserHandle);
+                    if (locationUri != null) {
+                        values.put(Calls.LOCATION, locationUri.toString());
+                    } else {
+                        values.put(Calls.LOCATION, (String) null);
+                    }
+                }
+
                 result = addEntryAndRemoveExpiredEntries(context, userManager, targetUserHandle,
                         values);
             }
@@ -1481,6 +1939,27 @@
             }
         }
 
+        private static Uri maybeInsertLocation(AddCallParams params, ContentResolver resolver,
+                UserHandle user) {
+            if (Double.isNaN(params.mLatitude) || Double.isNaN(params.mLongitude)) {
+                return null;
+            }
+            ContentValues locationValues = new ContentValues();
+            locationValues.put(Locations.LATITUDE, params.mLatitude);
+            locationValues.put(Locations.LONGITUDE, params.mLongitude);
+            Uri locationUri = ContentProvider.maybeAddUserId(Locations.CONTENT_URI,
+                    user.getIdentifier());
+            try {
+                return resolver.insert(locationUri, locationValues);
+            } catch (SecurityException e) {
+                // This can happen if the caller doesn't have location permissions. If that's the
+                // case just skip the insertion.
+                Log.w(LOG_TAG, "Skipping inserting location because caller lacks"
+                        + " ACCESS_FINE_LOCATION.");
+                return null;
+            }
+        }
+
         private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) {
             final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
                     .appendPath(dataId)
@@ -1587,4 +2066,47 @@
             return missedReason >= (USER_MISSED_NO_ANSWER);
         }
     }
+
+    /**
+     * Table that contains information on location data sent via call composer.
+     *
+     * All fields in this table require the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+     * permission for access.
+     */
+    public static class Locations implements BaseColumns {
+        private Locations() {}
+        /**
+         * Authority for the locations content provider.
+         */
+        public static final String AUTHORITY = "call_composer_locations";
+
+        /**
+         * Content type for the location table.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_composer_location";
+
+        /**
+         * Content type for the location entries.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/call_composer_location";
+
+        /**
+         * The content URI for this table
+         */
+        @NonNull
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
+
+        /**
+         * Latitude in degrees. See {@link android.location.Location#setLatitude(double)}.
+         * <p>Type: REAL</p>
+         */
+        public static final String LATITUDE = "latitude";
+
+        /**
+         * Longitude in degrees. See {@link android.location.Location#setLongitude(double)}.
+         * <p>Type: REAL</p>
+         */
+        public static final String LONGITUDE = "longitude";
+    }
 }
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 4ead3fc..e0f3018 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -56,5 +56,6 @@
     void onNotificationDirectReply(String key);
     void onSuggestedReplySent(String key, in CharSequence reply, int source);
     void onActionClicked(String key, in Notification.Action action, int source);
+    void onNotificationClicked(String key);
     void onAllowedAdjustmentsChanged();
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 6320149..cf2152c 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -242,6 +242,13 @@
     }
 
     /**
+     * Implement this to know when a notification is clicked by user.
+     * @param key the notification key
+     */
+    public void onNotificationClicked(@NonNull String key) {
+    }
+
+    /**
      * Implement this to know when a user has changed which features of
      * their notifications the assistant can modify.
      * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what
@@ -422,6 +429,13 @@
         }
 
         @Override
+        public void onNotificationClicked(String key) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_CLICKED, args).sendToTarget();
+        }
+
+        @Override
         public void onAllowedAdjustmentsChanged() {
             mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget();
         }
@@ -445,6 +459,7 @@
         public static final int MSG_ON_PANEL_REVEALED = 9;
         public static final int MSG_ON_PANEL_HIDDEN = 10;
         public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 11;
+        public static final int MSG_ON_NOTIFICATION_CLICKED = 12;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -550,6 +565,13 @@
                     onNotificationVisibilityChanged(key, isVisible);
                     break;
                 }
+                case MSG_ON_NOTIFICATION_CLICKED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    args.recycle();
+                    onNotificationClicked(key);
+                    break;
+                }
             }
         }
     }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index f79b59f..01e5260 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1487,6 +1487,11 @@
         }
 
         @Override
+        public void onNotificationClicked(String key) {
+            // no-op in the listener
+        }
+
+        @Override
         public void onAllowedAdjustmentsChanged() {
             // no-op in the listener
         }
diff --git a/core/java/android/service/textservice/OWNERS b/core/java/android/service/textservice/OWNERS
index 10b8b76..0471e29 100644
--- a/core/java/android/service/textservice/OWNERS
+++ b/core/java/android/service/textservice/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 34867
+# Bug component: 816455
 
-include ../../inputmethodservice/OWNERS
\ No newline at end of file
+include /services/core/java/com/android/server/textservices/OWNERS
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 63ee927..aa05787 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -225,7 +225,7 @@
     private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
             int transformHint);
     private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
-                                                      IBinder focusedToken, int displayId);
+            String windowName, IBinder focusedToken, String focusedWindowName, int displayId);
     private static native void nativeSetFrameTimelineVsync(long transactionObj,
             long frameTimelineVsyncId);
     private static native void nativeAddJankDataListener(long nativeListener,
@@ -3282,8 +3282,10 @@
          *
          * @hide
          */
-        public Transaction setFocusedWindow(@NonNull IBinder token, int displayId) {
-            nativeSetFocusedWindow(mNativeObject, token,  null /* focusedToken */, displayId);
+        public Transaction setFocusedWindow(@NonNull IBinder token, String windowName,
+                int displayId) {
+            nativeSetFocusedWindow(mNativeObject, token,  windowName,
+                    null /* focusedToken */, null /* focusedWindowName */, displayId);
             return this;
         }
 
@@ -3298,9 +3300,12 @@
          * @hide
          */
         public Transaction requestFocusTransfer(@NonNull IBinder token,
+                                                String windowName,
                                                 @NonNull IBinder focusedToken,
+                                                String focusedWindowName,
                                                 int displayId) {
-            nativeSetFocusedWindow(mNativeObject, token, focusedToken, displayId);
+            nativeSetFocusedWindow(mNativeObject, token, windowName, focusedToken,
+                    focusedWindowName, displayId);
             return this;
         }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7ac57b5..f603ef7 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1193,11 +1193,9 @@
 
     private void setBufferSize(Transaction transaction) {
         if (mUseBlastAdapter) {
-            mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth,
-                    mSurfaceHeight);
+            mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
         } else {
-            transaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
-                    mSurfaceHeight);
+            transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight);
         }
     }
 
@@ -1241,15 +1239,14 @@
                     .setName(name + "(BLAST)")
                     .setLocalOwnerView(this)
                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
-                    .setFormat(mFormat)
                     .setParent(mSurfaceControl)
                     .setFlags(mSurfaceFlags)
                     .setHidden(false)
                     .setBLASTLayer()
                     .setCallsite("SurfaceView.updateSurface")
                     .build();
-            mBlastBufferQueue = new BLASTBufferQueue(name,
-                    mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, true /* TODO */);
+            mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
+                    mSurfaceHeight, mFormat, true /* TODO */);
         } else {
             previousSurfaceControl = mSurfaceControl;
             mSurfaceControl = builder
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d694962..96d7304 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1852,20 +1852,22 @@
        return mBoundsLayer;
     }
 
-    Surface getOrCreateBLASTSurface(int width, int height) {
+    Surface getOrCreateBLASTSurface(int width, int height,
+            @Nullable WindowManager.LayoutParams params) {
         if (!mSurfaceControl.isValid()) {
             return null;
         }
 
+        int format = params == null ? PixelFormat.TRANSLUCENT : params.format;
         Surface ret = null;
         if (mBlastBufferQueue == null) {
-            mBlastBufferQueue = new BLASTBufferQueue(mTag,
-                    mSurfaceControl, width, height, mEnableTripleBuffering);
+            mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, width, height,
+                    format, mEnableTripleBuffering);
             // We only return the Surface the first time, as otherwise
             // it hasn't changed and there is no need to update.
             ret = mBlastBufferQueue.createSurface();
         } else {
-            mBlastBufferQueue.update(mSurfaceControl, width, height);
+            mBlastBufferQueue.update(mSurfaceControl, width, height, format);
         }
 
         return ret;
@@ -7608,8 +7610,8 @@
             if (!useBLAST()) {
                 mSurface.copyFrom(mSurfaceControl);
             } else {
-                final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
-                    mSurfaceSize.y);
+                final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x, mSurfaceSize.y,
+                        params);
                 // If blastSurface == null that means it hasn't changed since the last time we
                 // called. In this situation, avoid calling transferFrom as we would then
                 // inc the generation ID and cause EGL resources to be recreated.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0a4b784..5140c09 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2926,7 +2926,10 @@
                 ? SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES
                 : SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
         try {
-            mService.showInputMethodPickerFromSystem(mClient, mode, displayId);
+            final Completable.Void value = Completable.createVoid();
+            mService.showInputMethodPickerFromSystem(
+                    mClient, mode, displayId, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2934,7 +2937,10 @@
 
     private void showInputMethodPickerLocked() {
         try {
-            mService.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO);
+            final Completable.Void value = Completable.createVoid();
+            mService.showInputMethodPickerFromClient(
+                    mClient, SHOW_IM_PICKER_MODE_AUTO, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2970,7 +2976,10 @@
      */
     public void showInputMethodAndSubtypeEnabler(String imiId) {
         try {
-            mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
+            final Completable.Void value = Completable.createVoid();
+            mService.showInputMethodAndSubtypeEnablerFromClient(
+                    mClient, imiId, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3132,7 +3141,10 @@
                 matrixValues = new float[9];
                 matrix.getValues(matrixValues);
             }
-            mService.reportActivityView(mClient, childDisplayId, matrixValues);
+            final Completable.Void value = Completable.createVoid();
+            mService.reportActivityView(
+                    mClient, childDisplayId, matrixValues, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/textservice/OWNERS b/core/java/android/view/textservice/OWNERS
index 582be8d..0471e29 100644
--- a/core/java/android/view/textservice/OWNERS
+++ b/core/java/android/view/textservice/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 34867
+# Bug component: 816455
 
-include ../inputmethod/OWNERS
+include /services/core/java/com/android/server/textservices/OWNERS
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 6bc3110..f6cd7e2 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -323,6 +323,21 @@
     }
 
     /**
+     * Sets to containers adjacent to each other. Containers below two visible adjacent roots will
+     * be made invisible. This currently only applies to Task containers created by organizer.
+     * @param root1 the first root.
+     * @param root2 the second root.
+     */
+    @NonNull
+    public WindowContainerTransaction setAdjacentRoots(
+            @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
+        mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
+                root1.asBinder(),
+                root2.asBinder()));
+        return this;
+    }
+
+    /**
      * Merges another WCT into this one.
      * @param transfer When true, this will transfer everything from other potentially leaving
      *                 other in an unusable state. When false, other is left alone, but
@@ -642,6 +657,7 @@
         public static final int HIERARCHY_OP_TYPE_REORDER = 1;
         public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
         public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3;
+        public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
 
         private final int mType;
 
@@ -680,6 +696,11 @@
                     container, null, windowingModes, activityTypes, false);
         }
 
+        public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
+            return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
+                    root1, root2, null, null, false);
+        }
+
         private HierarchyOp(int type, @NonNull IBinder container, @Nullable IBinder reparent,
                 int[] windowingModes, int[] activityTypes, boolean toTop) {
             mType = type;
@@ -728,6 +749,11 @@
             return mContainer;
         }
 
+        @NonNull
+        public IBinder getAdjacentRoot() {
+            return mReparent;
+        }
+
         public boolean getToTop() {
             return mToTop;
         }
@@ -756,6 +782,9 @@
                             + mReparent + "}";
                 case HIERARCHY_OP_TYPE_REORDER:
                     return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+                case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+                    return "{SetAdjacentRoot: container=" + mContainer
+                            + " adjacentRoot=" + mReparent + "}";
                 default:
                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                             + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index cc266d6..a5eb5f6 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -22,17 +22,16 @@
 
 parcelable CompatibilityChangeConfig;
 parcelable CompatibilityChangeInfo;
-
 /**
  * Platform private API for talking with the PlatformCompat service.
  *
- * <p> Should be used for gating and logging from non-app processes.
- * For app processes please use android.compat.Compatibility API.
+ * <p>Should be used for gating and logging from non-app processes.
+ *
+ * <p>Note: for app processes please use {@code android.compat.Compatibility} API.
  *
  * {@hide}
  */
-interface IPlatformCompat
-{
+interface IPlatformCompat {
 
     /**
      * Reports that a compatibility change is affecting an app process now.
@@ -40,8 +39,9 @@
      * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
      * you do not need to call this API directly. The change will be reported for you.
      *
-     * @param changeId The ID of the compatibility change taking effect.
-     * @param appInfo  Representing the affected app.
+     * @param changeId the ID of the compatibility change taking effect
+     * @param appInfo  representing the affected app
+     * @throws SecurityException if logging is not allowed
      */
     void reportChange(long changeId, in ApplicationInfo appInfo);
 
@@ -51,11 +51,12 @@
      * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)},
      * you do not need to call this API directly. The change will be reported for you.
      *
-     * @param changeId    The ID of the compatibility change taking effect.
-     * @param userId      The ID of the user that the operation is done for.
-     * @param packageName The package name of the app in question.
+     * @param changeId    the ID of the compatibility change taking effect
+     * @param userId      the ID of the user that the operation is done for
+     * @param packageName the package name of the app in question
+     * @throws SecurityException if logging is not allowed
      */
-     void reportChangeByPackageName(long changeId, in String packageName, int userId);
+    void reportChangeByPackageName(long changeId, in String packageName, int userId);
 
     /**
      * Reports that a compatibility change is affecting an app process now.
@@ -63,13 +64,14 @@
      * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)},
      * you do not need to call this API directly. The change will be reported for you.
      *
-     * @param changeId The ID of the compatibility change taking effect.
-     * @param uid      The UID of the app in question.
+     * @param changeId the ID of the compatibility change taking effect
+     * @param uid      the UID of the app in question
+     * @throws SecurityException if logging is not allowed
      */
     void reportChangeByUid(long changeId, int uid);
 
     /**
-     * Query if a given compatibility change is enabled for an app process. This method should
+     * Queries if a given compatibility change is enabled for an app process. This method should
      * be called when implementing functionality on behalf of the affected app.
      *
      * <p>If this method returns {@code true}, the calling code should implement the compatibility
@@ -79,14 +81,15 @@
      * <p>It will also report the change as {@link #reportChange(long, ApplicationInfo)} would, so
      * there is no need to call that method directly.
      *
-     * @param changeId The ID of the compatibility change in question.
-     * @param appInfo  Representing the app in question.
-     * @return {@code true} if the change is enabled for the current app.
+     * @param changeId the ID of the compatibility change in question
+     * @param appInfo  representing the app in question
+     * @return {@code true} if the change is enabled for the current app
+     * @throws SecurityException if logging or reading compat confis is not allowed
      */
     boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
 
     /**
-     * Query if a given compatibility change is enabled for an app process. This method should
+     * Queries if a given compatibility change is enabled for an app process. This method should
      * be called when implementing functionality on behalf of the affected app.
      *
      * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name
@@ -102,15 +105,16 @@
      * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is
      * no need to call that method directly.
      *
-     * @param changeId    The ID of the compatibility change in question.
-     * @param packageName The package name of the app in question.
-     * @param userId      The ID of the user that the operation is done for.
-     * @return {@code true} if the change is enabled for the current app.
+     * @param changeId    the ID of the compatibility change in question
+     * @param packageName the package name of the app in question
+     * @param userId      the ID of the user that the operation is done for
+     * @return {@code true} if the change is enabled for the current app
+     * @throws SecurityException if logging or reading compat confis is not allowed
      */
     boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId);
 
     /**
-     * Query if a given compatibility change is enabled for an app process. This method should
+     * Queries if a given compatibility change is enabled for an app process. This method should
      * be called when implementing functionality on behalf of the affected app.
      *
      * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a uid
@@ -127,110 +131,132 @@
      * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is
      * no need to call that method directly.
      *
-     * @param changeId The ID of the compatibility change in question.
-     * @param uid      The UID of the app in question.
-     * @return {@code true} if the change is enabled for the current app.
+     * @param changeId the ID of the compatibility change in question
+     * @param uid      the UID of the app in question
+     * @return {@code true} if the change is enabled for the current app
+     * @throws SecurityException if logging or reading compat confis is not allowed
      */
     boolean isChangeEnabledByUid(long changeId, int uid);
 
     /**
-     * Add overrides to compatibility changes. Kills the app to allow the changes to take effect.
+     * Adds overrides to compatibility changes.
      *
-     * @param overrides Parcelable containing the compat change overrides to be applied.
-     * @param packageName The package name of the app whose changes will be overridden.
+     * <p>Kills the app to allow the changes to take effect.
      *
+     * @param overrides   parcelable containing the compat change overrides to be applied
+     * @param packageName the package name of the app whose changes will be overridden
+     * @throws SecurityException if overriding changes is not permitted
      */
     void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
 
     /**
-     * Add overrides to compatibility changes. Doesn't kill the app, to be only used in tests.
+     * Adds overrides to compatibility changes.
      *
-     * @param overrides Parcelable containing the compat change overrides to be applied.
-     * @param packageName The package name of the app whose changes will be overridden.
+     * <p>Does not kill the app, to be only used in tests.
      *
+     * @param overrides   parcelable containing the compat change overrides to be applied
+     * @param packageName the package name of the app whose changes will be overridden
+     * @throws SecurityException if overriding changes is not permitted.
      */
     void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName);
 
     /**
-     * Removes an override previously added via {@link #setOverrides(CompatibilityChangeConfig,
-     * String)}. This restores the default behaviour for the given change and app, once any app
-     * processes have been restarted.
-     * Kills the app to allow the changes to take effect.
+     * Restores the default behaviour for the given change and app.
      *
-     * @param changeId    The ID of the change that was overridden.
-     * @param packageName The app package name that was overridden.
-     * @return {@code true} if an override existed;
+     * <p>Kills the app to allow the changes to take effect.
+     *
+     * @param changeId    the ID of the change that was overridden
+     * @param packageName the app package name that was overridden
+     * @return {@code true} if an override existed
+     * @throws SecurityException if overriding changes is not permitted
      */
     boolean clearOverride(long changeId, String packageName);
 
     /**
-     * Enable all compatibility changes which have enabledSinceTargetSdk ==
-     * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the
-     * changes to take effect.
+     * Restores the default behaviour for the given change and app.
      *
-     * @param packageName The package name of the app whose compatibility changes will be enabled.
+     * <p>Does not kill the app; to be only used in tests.
+     *
+     * @param changeId    the ID of the change that was overridden
+     * @param packageName the app package name that was overridden
+     * @throws SecurityException if overriding changes is not permitted
+     */
+    void clearOverrideForTest(long changeId, String packageName);
+
+    /**
+     * Enables all compatibility changes that have enabledSinceTargetSdk ==
+     * {@param targetSdkVersion} for an app, subject to the policy.
+     *
+     * <p>Kills the app to allow the changes to take effect.
+     *
+     * @param packageName      The package name of the app whose compatibility changes will be
+     *                         enabled.
      * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled.
-     *
      * @return The number of changes that were enabled.
+     * @throws SecurityException if overriding changes is not permitted.
      */
     int enableTargetSdkChanges(in String packageName, int targetSdkVersion);
 
     /**
-     * Disable all compatibility changes which have enabledAfterTargetSdk ==
-     * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the
-     * changes to take effect.
+     * Disables all compatibility changes that have enabledAfterTargetSdk ==
+     * {@param targetSdkVersion} for an app, subject to the policy.
      *
-     * @param packageName The package name of the app whose compatibility changes will be disabled.
-     * @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled.
+     * <p>Kills the app to allow the changes to take effect.
      *
-     * @return The number of changes that were disabled.
+     * @param packageName      the package name of the app whose compatibility changes will be
+     *                         disabled
+     * @param targetSdkVersion the targetSdkVersion for filtering the changes to be disabled
+     * @return the number of changes that were disabled
+     * @throws SecurityException if overriding changes is not permitted.
      */
     int disableTargetSdkChanges(in String packageName, int targetSdkVersion);
 
     /**
-     * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect.
+     * Restores the default behaviour for the given app.
      *
-     * @param packageName The package name of the app whose overrides will be cleared.
+     * <p>Kills the app to allow the changes to take effect.
      *
+     * @param packageName the package name of the app whose overrides will be cleared
+     * @throws SecurityException if overriding changes is not permitted
      */
     void clearOverrides(in String packageName);
 
     /**
-     * Revert overrides to compatibility changes. Doesn't kill the app, to be only used in tests.
+     * Restores the default behaviour for the given app.
      *
-     * @param packageName The package name of the app whose overrides will be cleared.
+     * <p>Does not kill the app; to be only used in tests.
      *
+     * @param packageName the package name of the app whose overrides will be cleared
+     * @throws SecurityException if overriding changes is not permitted
      */
     void clearOverridesForTest(in String packageName);
 
-
     /**
      * Get configs for an application.
      *
-     * @param appInfo The application whose config will be returned.
-     *
-     * @return A {@link CompatibilityChangeConfig}, representing whether a change is enabled for
-     *         the given app or not.
+     * @param appInfo the application whose config will be returned
+     * @return a {@link CompatibilityChangeConfig}, representing whether a change is enabled for
+     * the given app or not
      */
     CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo);
 
     /**
      * List all compatibility changes.
      *
-     * @return An array of {@link CompatChangeInfo} known to the service.
+     * @return an array of {@link CompatibilityChangeInfo} known to the service
      */
     CompatibilityChangeInfo[] listAllChanges();
 
     /**
-    * List the compatibility changes that should be present in the UI.
-    * Filters out certain changes like e.g. logging only.
-    *
-    * @return An array of {@link CompatChangeInfo}.
-    */
+     * List the compatibility changes that should be present in the UI.
+     * Filters out certain changes like e.g. logging only.
+     *
+     * @return an array of {@link CompatibilityChangeInfo}
+     */
     CompatibilityChangeInfo[] listUIChanges();
 
     /**
-     * Get an instance that can determine whether a changeid can be overridden for a package name.
+     * Gets an instance that can determine whether a changeid can be overridden for a package name.
      */
     IOverrideValidator getOverrideValidator();
 }
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
index e9e39db..2113173 100644
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -205,14 +205,14 @@
      * A utility method using given {@link IVoidResultCallback} to callback the result.
      *
      * @param callback {@link IVoidResultCallback} to be called back.
-     * @param resultSupplier the supplier from which the result is provided.
+     * @param runnable to execute the given method
      */
     public static void onResult(@NonNull IVoidResultCallback callback,
-            @NonNull Supplier<Void> resultSupplier) {
+            @NonNull Runnable runnable) {
         Throwable exception = null;
 
         try {
-            resultSupplier.get();
+            runnable.run();
         } catch (Throwable throwable) {
             exception = throwable;
         }
diff --git a/core/java/com/android/internal/textservice/OWNERS b/core/java/com/android/internal/textservice/OWNERS
new file mode 100644
index 0000000..0471e29
--- /dev/null
+++ b/core/java/com/android/internal/textservice/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 816455
+
+include /services/core/java/com/android/server/textservices/OWNERS
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1fadfc5..b42404f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -30,6 +30,7 @@
 import com.android.internal.inputmethod.IInputMethodSubtypeResultCallback;
 import com.android.internal.inputmethod.IInputMethodSubtypeListResultCallback;
 import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.IVoidResultCallback;
 
 /**
  * Public interface to the global input method manager, used by all client
@@ -66,10 +67,11 @@
             in IInputBindResultResultCallback inputBindResult);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
-            int auxiliarySubtypeMode);
+            int auxiliarySubtypeMode, in IVoidResultCallback resultCallback);
     void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode,
-            int displayId);
-    void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
+            int displayId, in IVoidResultCallback resultCallback);
+    void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId,
+            in IVoidResultCallback resultCallback);
     void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback);
     void getCurrentInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
     void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
@@ -78,7 +80,7 @@
     void getInputMethodWindowVisibleHeight(IIntResultCallback resultCallback);
 
     void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
-            in float[] matrixValues);
+            in float[] matrixValues, in IVoidResultCallback resultCallback);
 
     oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
     /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9712b4e..a161f18 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -307,8 +307,24 @@
         return getDevicePolicyManager().getPasswordMaximumLength(quality);
     }
 
+    /**
+     * Returns aggregated (legacy) password quality requirement on the target user from all admins.
+     */
     public PasswordMetrics getRequestedPasswordMetrics(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumMetrics(userId);
+        return getRequestedPasswordMetrics(userId, false);
+    }
+
+    /**
+     * Returns aggregated (legacy) password quality requirement on the target user from all admins,
+     * optioanlly disregarding policies set on the managed profile as if the  profile had separate
+     * work challenge.
+     */
+    public PasswordMetrics getRequestedPasswordMetrics(int userId, boolean deviceWideOnly) {
+        return getDevicePolicyManager().getPasswordMinimumMetrics(userId, deviceWideOnly);
+    }
+
+    private int getRequestedPasswordHistoryLength(int userId) {
+        return getDevicePolicyManager().getPasswordHistoryLength(null, userId);
     }
 
     /**
@@ -317,39 +333,21 @@
      * @return complexity level for the user.
      */
     public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) {
-        return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId);
+        return getRequestedPasswordComplexity(userId, false);
     }
 
-    public int getRequestedPasswordQuality(int userId) {
-        return getDevicePolicyManager().getPasswordQuality(null, userId);
-    }
+    /**
+     * Returns the effective complexity for the user, optioanlly disregarding complexity set on the
+     * managed profile as if the  profile had separate work challenge.
 
-    private int getRequestedPasswordHistoryLength(int userId) {
-        return getDevicePolicyManager().getPasswordHistoryLength(null, userId);
-    }
-
-    public int getRequestedPasswordMinimumLetters(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumLetters(null, userId);
-    }
-
-    public int getRequestedPasswordMinimumUpperCase(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumUpperCase(null, userId);
-    }
-
-    public int getRequestedPasswordMinimumLowerCase(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumLowerCase(null, userId);
-    }
-
-    public int getRequestedPasswordMinimumNumeric(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumNumeric(null, userId);
-    }
-
-    public int getRequestedPasswordMinimumSymbols(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumSymbols(null, userId);
-    }
-
-    public int getRequestedPasswordMinimumNonLetter(int userId) {
-        return getDevicePolicyManager().getPasswordMinimumNonLetter(null, userId);
+     * @param userId  The user to return the complexity for.
+     * @param deviceWideOnly  whether to ignore complexity set on the managed profile.
+     * @return complexity level for the user.
+     */
+    public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId,
+            boolean deviceWideOnly) {
+        return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId,
+                deviceWideOnly);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index a761b4c..e5bc470 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -94,6 +94,9 @@
     // property for runtime configuration differentiation in vendor
     private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku";
 
+    // property for background blur support in surface flinger
+    private static final String BLUR_PROPERTY = "ro.surface_flinger.supports_background_blur";
+
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
     int[] mGlobalGids = EmptyArray.INT;
 
@@ -1242,6 +1245,10 @@
             addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
         }
 
+        if (SystemProperties.get(BLUR_PROPERTY, "default").equals("1")) {
+            addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0);
+        }
+
         for (String featureName : mUnavailableFeatures) {
             removeFeature(featureName);
         }
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 8d3faf4..b790056 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -68,7 +68,7 @@
 };
 
 static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
-                          jlong width, jlong height, jboolean enableTripleBuffering) {
+                          jlong width, jlong height, jint format, jboolean enableTripleBuffering) {
     String8 str8;
     if (jName) {
         const jchar* str16 = env->GetStringCritical(jName, nullptr);
@@ -81,7 +81,7 @@
     std::string name = str8.string();
     sp<BLASTBufferQueue> queue =
             new BLASTBufferQueue(name, reinterpret_cast<SurfaceControl*>(surfaceControl), width,
-                                 height, enableTripleBuffering);
+                                 height, format, enableTripleBuffering);
     queue->incStrong((void*)nativeCreate);
     return reinterpret_cast<jlong>(queue.get());
 }
@@ -104,9 +104,10 @@
     queue->setNextTransaction(transaction);
 }
 
-static void nativeUpdate(JNIEnv*env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, jlong height) {
+static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
+                         jlong height, jint format) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
-    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
 }
 
 static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
@@ -139,11 +140,11 @@
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         // clang-format off
-        {"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate},
+        {"nativeCreate", "(Ljava/lang/String;JJJIZ)J", (void*)nativeCreate},
         {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
-        {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate},
+        {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
         {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue},
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
         {"nativeSetTransactionCompleteCallback",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b8e1807..7c670e1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1593,7 +1593,9 @@
 }
 
 static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
-                                   jobject toTokenObj, jobject focusedTokenObj, jint displayId) {
+                                   jobject toTokenObj, jstring windowNameJstr,
+                                   jobject focusedTokenObj, jstring focusedWindowNameJstr,
+                                   jint displayId) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     if (toTokenObj == NULL) return;
 
@@ -1602,8 +1604,22 @@
     if (focusedTokenObj != NULL) {
         focusedToken = ibinderForJavaObject(env, focusedTokenObj);
     }
-    transaction->setFocusedWindow(toToken, focusedToken, systemTime(SYSTEM_TIME_MONOTONIC),
-                                  displayId);
+
+    FocusRequest request;
+    request.token = toToken;
+    if (windowNameJstr != NULL) {
+        ScopedUtfChars windowName(env, windowNameJstr);
+        request.windowName = windowName.c_str();
+    }
+
+    request.focusedToken = focusedToken;
+    if (focusedWindowNameJstr != NULL) {
+        ScopedUtfChars focusedWindowName(env, focusedWindowNameJstr);
+        request.focusedWindowName = focusedWindowName.c_str();
+    }
+    request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+    request.displayId = displayId;
+    transaction->setFocusedWindow(request);
 }
 
 static void nativeSetFrameTimelineVsync(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -1865,7 +1881,7 @@
             (void*)nativeGetHandle },
     {"nativeSetFixedTransformHint", "(JJI)V",
             (void*)nativeSetFixedTransformHint},
-    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Landroid/os/IBinder;I)V",
+    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V",
             (void*)nativeSetFocusedWindow},
     {"nativeSetFrameTimelineVsync", "(JJ)V",
             (void*)nativeSetFrameTimelineVsync },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4dc8121..c76a5d3 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1533,7 +1533,6 @@
     jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
     jstring managed_nice_name, fail_fn_t fail_fn) {
 
-  ensureInAppMountNamespace(fail_fn);
   std::vector<std::string> merged_data_info_list;
   insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
           process_name, managed_nice_name, fail_fn);
@@ -1680,10 +1679,11 @@
 
   MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
 
-  // System services, isolated process, webview/app zygote, old target sdk app, should
-  // give a null in same_uid_pkgs and private_volumes so they don't need app data isolation.
-  // Isolated process / webview / app zygote should be gated by SELinux and file permission
-  // so they can't even traverse CE / DE directories.
+  // Make sure app is running in its own mount namespace before isolating its data directories.
+  ensureInAppMountNamespace(fail_fn);
+
+  // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind
+  // mount all related packages separately.
   if (mount_data_dirs) {
     isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
             uid, process_name, managed_nice_name, fail_fn);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c9579ed..76acc00 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -322,8 +322,10 @@
     <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
 
     <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
+    <protected-broadcast android:name="android.nfc.action.ALWAYS_ON_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
+    <protected-broadcast android:name="android.nfc.action.REQUIRE_UNLOCK_FOR_NFC" />
     <protected-broadcast android:name="com.android.nfc.action.LLCP_UP" />
     <protected-broadcast android:name="com.android.nfc.action.LLCP_DOWN" />
     <protected-broadcast android:name="com.android.nfc.cardemulation.action.CLOSE_TAP_DIALOG" />
@@ -434,6 +436,8 @@
 
     <protected-broadcast android:name="android.location.PROVIDERS_CHANGED" />
     <protected-broadcast android:name="android.location.MODE_CHANGED" />
+    <protected-broadcast android:name="android.location.action.GNSS_CAPABILITIES_CHANGED" />
+
     <protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
 
     <protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
@@ -1691,7 +1695,7 @@
     <permission android:name="android.permission.MANAGE_IPSEC_TUNNELS"
         android:protectionLevel="signature|appop" />
 
-    <!-- @hide Allows apps to create and manage Test Networks.
+    <!-- @SystemApi @hide Allows apps to create and manage Test Networks.
          <p>Granted only to shell. CTS tests will use
          UiAutomation.AdoptShellPermissionIdentity() to gain access.
     -->
@@ -5383,6 +5387,9 @@
          on-device data -->
     <attribution android:tag="OfflineLocationTimeZoneProviderService"
                  android:label="@string/offline_location_time_zone_detection_service_attribution"/>
+    <!-- Attribution for Gnss Time Update service. -->
+    <attribution android:tag="GnssTimeUpdateService"
+                 android:label="@string/gnss_time_update_service"/>
 
     <application android:process="system"
                  android:persistent="true"
diff --git a/packages/SystemUI/res/drawable/ic_camera_blocked.xml b/core/res/res/drawable/ic_camera_blocked.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_camera_blocked.xml
rename to core/res/res/drawable/ic_camera_blocked.xml
diff --git a/packages/SystemUI/res/drawable/ic_mic_blocked.xml b/core/res/res/drawable/ic_mic_blocked.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_mic_blocked.xml
rename to core/res/res/drawable/ic_mic_blocked.xml
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index b558087..e46147e 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -33,14 +33,14 @@
     <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
     <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
 
-    <color name="accent_device_default_light">@color/system_accent_500</color>
+    <color name="accent_device_default_light">@color/system_accent_600</color>
     <color name="accent_device_default_dark">@color/system_accent_200</color>
     <color name="accent_device_default">@color/accent_device_default_light</color>
 
     <color name="background_device_default_dark">@color/system_main_900</color>
-    <color name="background_device_default_light">@color/system_main_100</color>
+    <color name="background_device_default_light">@color/system_main_50</color>
     <color name="background_floating_device_default_dark">@color/system_main_800</color>
-    <color name="background_floating_device_default_light">@color/system_main_200</color>
+    <color name="background_floating_device_default_light">@color/system_main_100</color>
 
     <!-- Error color -->
     <color name="error_color_device_default_dark">@color/error_color_material_dark</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 272e130..7f6053e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1577,6 +1577,10 @@
         <item>network</item>
     </string-array>
 
+    <!-- Enables the GnssTimeUpdate service. This is the global switch for enabling Gnss time based
+         suggestions to TimeDetector service. See also config_autoTimeSourcesPriority. -->
+    <bool name="config_enableGnssTimeUpdateService">false</bool>
+
     <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time
          zone update mechanism. -->
     <bool name="config_enableUpdateableTimeZoneRules">false</bool>
@@ -2649,6 +2653,11 @@
     <string name="config_usbResolverActivity" translatable="false"
             >com.android.systemui/com.android.systemui.usb.UsbResolverActivity</string>
 
+    <!-- Component name of the activity used to inform a user about a sensory being blocked because
+     of privacy settings. -->
+    <string name="config_sensorUseStartedActivity" translatable="false"
+            >com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
+
     <!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
     <string name="config_platformVpnConfirmDialogComponent" translatable="false"
             >com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
@@ -4001,13 +4010,13 @@
     <string name="config_misprovisionedBrandValue" translatable="false"></string>
 
     <!-- Pre-scale volume at volume step 1 for Absolute Volume -->
-    <fraction name="config_prescaleAbsoluteVolume_index1">50%</fraction>
+    <fraction name="config_prescaleAbsoluteVolume_index1">60%</fraction>
 
     <!-- Pre-scale volume at volume step 2 for Absolute Volume -->
-    <fraction name="config_prescaleAbsoluteVolume_index2">70%</fraction>
+    <fraction name="config_prescaleAbsoluteVolume_index2">80%</fraction>
 
     <!-- Pre-scale volume at volume step 3 for Absolute Volume -->
-    <fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
+    <fraction name="config_prescaleAbsoluteVolume_index3">90%</fraction>
 
     <!-- Whether or not the "SMS app service" feature is enabled -->
     <bool name="config_useSmsAppService">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 317a76f..996fbb3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -450,6 +450,8 @@
          understand which sub-unit of an application is requesting permissions and using power.
          [CHAR LIMIT=NONE]-->
     <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string>
+    <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]-->
+    <string name="gnss_time_update_service">GNSS Time Update Service</string>
 
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
@@ -5817,4 +5819,13 @@
     <!-- Notification action to dismiss. [CHAR LIMIT=50] -->
     <string name="dismiss_action">Dismiss</string>
 
+    <!--- Content of notification triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_start_use_mic_notification_content">To continue, &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="app" example="Gmail">%s</xliff:g>&lt;/b&gt; needs access to your device microphone.</string>
+    <!--- Content of notification triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_start_use_camera_notification_content">To continue, &lt;b&gt;<xliff:g id="app" example="Gmail">%s</xliff:g>&lt;/b&gt; needs access to your device’s camera.</string>
+    <!--- Action button in the dialog triggered if a sensor (e.g. microphone or camera) is disabled but an app tried to access it. [CHAR LIMIT=60] -->
+    <string name="sensor_privacy_start_use_dialog_turn_on_button">Turn on</string>
+    <!--- Label for notification channel for all sensor privacy related notifications. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_notification_channel_label">Sensor Privacy</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0c86905..e2271d1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -379,6 +379,7 @@
   <java-symbol type="string" name="config_usbAccessoryUriActivity" />
   <java-symbol type="string" name="config_usbConfirmActivity" />
   <java-symbol type="string" name="config_usbResolverActivity" />
+  <java-symbol type="string" name="config_sensorUseStartedActivity" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
@@ -2176,6 +2177,7 @@
   <java-symbol type="string" name="config_persistentDataPackageName" />
   <java-symbol type="string" name="config_deviceConfiguratorPackageName" />
   <java-symbol type="array" name="config_autoTimeSourcesPriority" />
+  <java-symbol type="bool" name="config_enableGnssTimeUpdateService" />
   <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" />
   <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" />
   <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
@@ -4150,4 +4152,11 @@
   <java-symbol type="bool" name="config_enableBackSound" />
 
   <java-symbol type="bool" name="config_forceOrientationListenerEnabledWhileDreaming" />
+
+  <java-symbol type="drawable" name="ic_camera_blocked" />
+  <java-symbol type="drawable" name="ic_mic_blocked" />
+  <java-symbol type="string" name="sensor_privacy_start_use_mic_notification_content" />
+  <java-symbol type="string" name="sensor_privacy_start_use_camera_notification_content" />
+  <java-symbol type="string" name="sensor_privacy_start_use_dialog_turn_on_button" />
+  <java-symbol type="string" name="sensor_privacy_notification_channel_label" />
 </resources>
diff --git a/core/tests/coretests/src/android/view/textservice/OWNERS b/core/tests/coretests/src/android/view/textservice/OWNERS
new file mode 100644
index 0000000..0471e29
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textservice/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 816455
+
+include /services/core/java/com/android/server/textservices/OWNERS
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 6019b90..597ea14 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -366,15 +366,6 @@
         }
 
         @Override
-        public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
-        }
-
-        @Override
-        public boolean isHdmiCecVolumeControlEnabled() {
-            return true;
-        }
-
-        @Override
         public void addHdmiCecVolumeControlFeatureListener(
                 IHdmiCecVolumeControlFeatureListener listener) {
         }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index eb5e0a2..c3d822c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -462,6 +462,8 @@
         <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
         <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
+        <!-- Permission required for CTS test - CallLogTest -->
+        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4914336..3aaf11c 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -28,11 +28,12 @@
     public long mNativeObject; // BLASTBufferQueue*
 
     private static native long nativeCreate(String name, long surfaceControl, long width,
-                                            long height, boolean tripleBufferingEnabled);
+            long height, int format, boolean tripleBufferingEnabled);
     private static native void nativeDestroy(long ptr);
     private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
     private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
-    private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+    private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
+            int format);
     private static native void nativeFlushShadowQueue(long ptr);
     private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
                                                               long frameNumber);
@@ -52,8 +53,9 @@
 
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
-            boolean tripleBufferingEnabled) {
-        mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, tripleBufferingEnabled);
+            @PixelFormat.Format int format, boolean tripleBufferingEnabled) {
+        mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, format,
+                tripleBufferingEnabled);
     }
 
     public void destroy() {
@@ -85,8 +87,15 @@
         nativeSetNextTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
     }
 
-    public void update(SurfaceControl sc, int width, int height) {
-        nativeUpdate(mNativeObject, sc.mNativeObject, width, height);
+    /**
+     * Updates {@link SurfaceControl}, size, and format for a particular BLASTBufferQueue
+     * @param sc The new SurfaceControl that this BLASTBufferQueue will update
+     * @param width The new width for the buffer.
+     * @param height The new height for the buffer.
+     * @param format The new format for the buffer.
+     */
+    public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {
+        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format);
     }
 
     /**
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 87ca960..7e68bc0 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -19,6 +19,7 @@
 import android.annotation.CheckResult;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -702,6 +703,40 @@
     }
 
     /**
+     * Splits this Rect into small rects of the same width.
+     * @hide
+     */
+    @TestApi
+    public void splitVertically(@NonNull Rect ...splits) {
+        final int count = splits.length;
+        final int splitWidth = width() / count;
+        for (int i = 0; i < count; i++) {
+            final Rect split = splits[i];
+            split.left = left + (splitWidth * i);
+            split.top = top;
+            split.right = split.left + splitWidth;
+            split.bottom = bottom;
+        }
+    }
+
+    /**
+     * Splits this Rect into small rects of the same height.
+     * @hide
+     */
+    @TestApi
+    public void splitHorizontally(@NonNull Rect ...outSplits) {
+        final int count = outSplits.length;
+        final int splitHeight = height() / count;
+        for (int i = 0; i < count; i++) {
+            final Rect split = outSplits[i];
+            split.left = left;
+            split.top = top + (splitHeight * i);
+            split.right = right;
+            split.bottom = split.top + splitHeight;
+        }
+    }
+
+    /**
      * Parcelable interface methods
      */
     @Override
diff --git a/keystore/java/android/security/AuthTokenUtils.java b/keystore/java/android/security/AuthTokenUtils.java
index 14d6626..e637600 100644
--- a/keystore/java/android/security/AuthTokenUtils.java
+++ b/keystore/java/android/security/AuthTokenUtils.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.hardware.security.keymint.HardwareAuthToken;
-import android.hardware.security.keymint.Timestamp;
+import android.hardware.security.secureclock.Timestamp;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 1fde2b5..fcc518c 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -17,11 +17,13 @@
 package android.security;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.security.keymint.HardwareAuthToken;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.security.authorization.IKeystoreAuthorization;
+import android.security.authorization.LockScreenEvent;
 import android.system.keystore2.ResponseCode;
 import android.util.Log;
 
@@ -75,4 +77,31 @@
         return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken));
     }
 
+    /**
+     * Informs keystore2 about lock screen event.
+     *
+     * @param locked            - whether it is a lock (true) or unlock (false) event
+     * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
+     *                          password provided by the LockSettingService
+     *
+     * @return 0 if successful or a {@code ResponseCode}.
+     */
+    public int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
+            @Nullable byte[] syntheticPassword) {
+        if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+        try {
+            if (locked) {
+                getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null);
+            } else {
+                getService().onLockScreenEvent(LockScreenEvent.UNLOCK, userId, syntheticPassword);
+            }
+            return 0;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Can not connect to keystore", e);
+            return SYSTEM_ERROR;
+        } catch (ServiceSpecificException e) {
+            return e.errorCode;
+        }
+    }
+
 }
diff --git a/libs/WindowManager/Shell/res/layout/bubble_overflow_activity.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml
similarity index 93%
rename from libs/WindowManager/Shell/res/layout/bubble_overflow_activity.xml
rename to libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml
index 3060619..8224d95 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_overflow_activity.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<com.android.wm.shell.bubbles.BubbleOverflowContainerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/bubble_overflow_container"
     android:layout_width="match_parent"
@@ -34,8 +34,8 @@
 
     <LinearLayout
         android:id="@+id/bubble_overflow_empty_state"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:paddingLeft="@dimen/bubble_overflow_empty_state_padding"
         android:paddingRight="@dimen/bubble_overflow_empty_state_padding"
         android:orientation="vertical"
@@ -69,4 +69,4 @@
             android:paddingBottom="@dimen/bubble_empty_overflow_subtitle_padding"
             android:gravity="center"/>
     </LinearLayout>
-</LinearLayout>
+</com.android.wm.shell.bubbles.BubbleOverflowContainerView>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index cb6b543..67f4cd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -24,7 +24,6 @@
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.wm.shell.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_CONTROLLER;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -37,14 +36,16 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Outline;
+import android.graphics.Picture;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
-import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
@@ -77,6 +78,7 @@
 
     private AlphaOptimizedButton mSettingsIcon;
     private TaskView mTaskView;
+    private BubbleOverflowContainerView mOverflowView;
 
     private int mTaskId = INVALID_TASK_ID;
 
@@ -125,6 +127,7 @@
             if (mDestroyed || mInitialized) {
                 return;
             }
+
             // Custom options so there is no activity transition animation
             ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
                     0 /* enterResId */, 0 /* exitResId */);
@@ -307,15 +310,25 @@
      * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
      * to be called after view inflate.
      */
-    void initialize(BubbleController controller, BubbleStackView stackView) {
+    void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow) {
         mController = controller;
         mStackView = stackView;
-
-        mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
-        mExpandedViewContainer.addView(mTaskView);
-        bringChildToFront(mTaskView);
-        mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
+        mIsOverflow = isOverflow;
         mPositioner = mController.getPositioner();
+
+        if (mIsOverflow) {
+            mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
+                    R.layout.bubble_overflow_container, null /* root */);
+            mOverflowView.setBubbleController(mController);
+            mExpandedViewContainer.addView(mOverflowView);
+            bringChildToFront(mOverflowView);
+            mSettingsIcon.setVisibility(GONE);
+        } else {
+            mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
+            mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
+            mExpandedViewContainer.addView(mTaskView);
+            bringChildToFront(mTaskView);
+        }
     }
 
     void updateDimensions() {
@@ -390,6 +403,17 @@
     /** Return a GraphicBuffer with the contents of the task view surface. */
     @Nullable
     SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
+        if (mIsOverflow) {
+            // For now, just snapshot the view and return it as a hw buffer so that the animation
+            // code for both the tasks and overflow can be the same
+            Picture p = new Picture();
+            mOverflowView.draw(
+                    p.beginRecording(mOverflowView.getWidth(), mOverflowView.getHeight()));
+            p.endRecording();
+            Bitmap snapshot = Bitmap.createBitmap(p);
+            return new SurfaceControl.ScreenshotHardwareBuffer(snapshot.getHardwareBuffer(),
+                    snapshot.getColorSpace(), false /* containsSecureLayers */);
+        }
         if (mTaskView == null || mTaskView.getSurfaceControl() == null) {
             return null;
         }
@@ -400,6 +424,11 @@
     }
 
     int[] getTaskViewLocationOnScreen() {
+        if (mIsOverflow) {
+            // This is only used for animating away the surface when switching bubbles, just use the
+            // view location on screen for now to allow us to use the same animation code with tasks
+            return mOverflowView.getLocationOnScreen();
+        }
         if (mTaskView != null) {
             return mTaskView.getLocationOnScreen();
         } else {
@@ -450,10 +479,7 @@
         final float alpha = visibility ? 1f : 0f;
 
         mPointerView.setAlpha(alpha);
-        if (mTaskView == null) {
-            return;
-        }
-        if (alpha != mTaskView.getAlpha()) {
+        if (mTaskView != null) {
             mTaskView.setAlpha(alpha);
         }
     }
@@ -467,19 +493,6 @@
         return mTaskId;
     }
 
-    public void setOverflow(boolean overflow) {
-        mIsOverflow = overflow;
-
-        Intent target = new Intent(mContext, BubbleOverflowActivity.class);
-        target.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
-        Bundle extras = new Bundle();
-        extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController));
-        target.putExtras(extras);
-        mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
-                target, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-        mSettingsIcon.setVisibility(GONE);
-    }
-
     /**
      * Sets the bubble used to populate this view.
      */
@@ -511,7 +524,8 @@
 
             if (isNew) {
                 mPendingIntent = mBubble.getBubbleIntent();
-                if (mPendingIntent != null || mBubble.hasMetadataShortcutId()) {
+                if ((mPendingIntent != null || mBubble.hasMetadataShortcutId())
+                        && mTaskView != null) {
                     setContentVisibility(false);
                     mTaskView.setVisibility(VISIBLE);
                 }
@@ -552,13 +566,19 @@
             desiredHeight = Math.max(desiredHeight, mMinHeight);
             float height = Math.min(desiredHeight, getMaxExpandedHeight());
             height = Math.max(height, mMinHeight);
-            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
+            FrameLayout.LayoutParams lp = mIsOverflow
+                    ? (FrameLayout.LayoutParams) mOverflowView.getLayoutParams()
+                    : (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
             mNeedsNewHeight = lp.height != height;
             if (!mImeVisible) {
                 // If the ime is visible... don't adjust the height because that will cause
                 // a configuration change and the ime will be lost.
                 lp.height = (int) height;
-                mTaskView.setLayoutParams(lp);
+                if (mIsOverflow) {
+                    mOverflowView.setLayoutParams(lp);
+                } else {
+                    mTaskView.setLayoutParams(lp);
+                }
                 mNeedsNewHeight = false;
             }
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -602,6 +622,9 @@
             updateHeight();
             mTaskView.onLocationChanged();
         }
+        if (mIsOverflow) {
+            mOverflowView.show();
+        }
     }
 
     /**
@@ -637,8 +660,8 @@
 
     /**
      * Cleans up anything related to the task and TaskView. If this view should be reused after this
-     * method is called, then {@link #initialize(BubbleController, BubbleStackView)} must be invoked
-     * first.
+     * method is called, then {@link #initialize(BubbleController, BubbleStackView, boolean)} must
+     * be invoked first.
      */
     public void cleanUpExpandedState() {
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -653,8 +676,6 @@
         }
         if (mTaskView != null) {
             mTaskView.release();
-        }
-        if (mTaskView != null) {
             removeView(mTaskView);
             mTaskView = null;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 8ab2f63..16cd3cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -65,8 +65,7 @@
 
     /** Call before use and again if cleanUpExpandedState was called.  */
     fun initialize(controller: BubbleController) {
-        getExpandedView()?.initialize(controller, controller.stackView)
-        getExpandedView()?.setOverflow(true)
+        getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
     }
 
     fun cleanUpExpandedState() {
@@ -86,7 +85,7 @@
         bitmapSize = positioner.bubbleBitmapSize
         iconBitmapSize = (bitmapSize * 0.46f).toInt()
         val bubbleSize = positioner.bubbleSize
-        overflowBtn?.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+        overflowBtn?.layoutParams = FrameLayout.LayoutParams(bubbleSize, bubbleSize)
         expandedView?.updateDimensions()
     }
 
@@ -96,7 +95,7 @@
         // Set overflow button accent color, dot color
         val typedValue = TypedValue()
         context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
-        val colorAccent = res.getColor(typedValue.resourceId)
+        val colorAccent = res.getColor(typedValue.resourceId, null)
         overflowBtn?.drawable?.setTint(colorAccent)
         dotColor = colorAccent
 
@@ -106,7 +105,7 @@
         val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
             == Configuration.UI_MODE_NIGHT_YES)
         val bg = ColorDrawable(res.getColor(
-            if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+            if (nightMode) R.color.bubbles_dark else R.color.bubbles_light, null))
 
         val fg = InsetDrawable(overflowBtn?.drawable,
             bitmapSize - iconBitmapSize /* inset */)
@@ -116,7 +115,7 @@
         // Update dot path
         dotPath = PathParser.createPathFromPathData(
             res.getString(com.android.internal.R.string.config_icon_mask))
-        val scale = iconFactory.normalizer.getScale(getIconView()!!.getDrawable(),
+        val scale = iconFactory.normalizer.getScale(iconView!!.drawable,
             null /* outBounds */, null /* path */, null /* outMaskShape */)
         val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
         val matrix = Matrix()
@@ -177,10 +176,10 @@
             overflowBtn = inflater.inflate(R.layout.bubble_overflow_button,
                     null /* root */, false /* attachToRoot */) as BadgedImageView
             overflowBtn?.initialize(positioner)
-            overflowBtn?.setContentDescription(context.resources.getString(
-                    R.string.bubble_overflow_button_content_description))
+            overflowBtn?.contentDescription = context.resources.getString(
+                    R.string.bubble_overflow_button_content_description)
             val bubbleSize = positioner.bubbleSize
-            overflowBtn?.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+            overflowBtn?.layoutParams = FrameLayout.LayoutParams(bubbleSize, bubbleSize)
             updateBtnTheme()
         }
         return overflowBtn
@@ -191,10 +190,10 @@
     }
 
     override fun getTaskId(): Int {
-        return if (expandedView != null) expandedView!!.getTaskId() else INVALID_TASK_ID
+        return if (expandedView != null) expandedView!!.taskId else INVALID_TASK_ID
     }
 
     companion object {
-        @JvmField val KEY = "Overflow"
+        const val KEY = "Overflow"
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
similarity index 87%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowActivity.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index cfd0066..39e4e1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -29,16 +29,21 @@
 import android.graphics.Color;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -50,10 +55,9 @@
 import java.util.function.Consumer;
 
 /**
- * Activity for showing aged out bubbles.
- * Must be public to be accessible to androidx...AppComponentFactory
+ * Container view for showing aged out bubbles.
  */
-public class BubbleOverflowActivity extends Activity {
+public class BubbleOverflowContainerView extends LinearLayout {
     static final String EXTRA_BUBBLE_CONTROLLER = "bubble_controller";
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
@@ -95,37 +99,55 @@
         }
     }
 
+    public BubbleOverflowContainerView(Context context) {
+        super(context);
+    }
+
+    public BubbleOverflowContainerView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BubbleOverflowContainerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public BubbleOverflowContainerView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setBubbleController(BubbleController controller) {
+        mController = controller;
+    }
+
+    public void show() {
+        setVisibility(View.VISIBLE);
+        updateOverflow();
+    }
+
+    public void hide() {
+        setVisibility(View.INVISIBLE);
+    }
+
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.bubble_overflow_activity);
+    protected void onFinishInflate() {
+        super.onFinishInflate();
 
         mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
         mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
         mEmptyStateTitle = findViewById(R.id.bubble_overflow_empty_title);
         mEmptyStateSubtitle = findViewById(R.id.bubble_overflow_empty_subtitle);
         mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
-
-        Intent intent = getIntent();
-        if (intent != null && intent.getExtras() != null) {
-            IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
-            if (binder instanceof ObjectWrapper) {
-                mController = ((ObjectWrapper<BubbleController>) binder).get();
-                updateOverflow();
-            }
-        } else {
-            Log.w(TAG, "Bubble overflow activity created without bubble controller!");
-        }
     }
 
     void updateOverflow() {
         Resources res = getResources();
         final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
         mRecyclerView.setLayoutManager(
-                new NoScrollGridLayoutManager(getApplicationContext(), columns));
+                new NoScrollGridLayoutManager(getContext(), columns));
 
         DisplayMetrics displayMetrics = new DisplayMetrics();
-        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+        getContext().getDisplay().getMetrics(displayMetrics);
 
         final int overflowPadding = res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
         final int recyclerViewWidth = displayMetrics.widthPixels - (overflowPadding * 2);
@@ -137,7 +159,7 @@
                 - res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
         final int viewHeight = recyclerViewHeight / rows;
 
-        mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
+        mAdapter = new BubbleOverflowAdapter(getContext(), mOverflowBubbles,
                 mController::promoteBubbleFromOverflow,
                 mController.getPositioner(),
                 viewWidth, viewHeight);
@@ -146,6 +168,11 @@
         mOverflowBubbles.clear();
         mOverflowBubbles.addAll(mController.getOverflowBubbles());
         mAdapter.notifyDataSetChanged();
+
+        // Currently BubbleExpandedView.mExpandedViewContainer is WRAP_CONTENT so use the same
+        // width we would use for the recycler view
+        LayoutParams lp = (LayoutParams) mEmptyState.getLayoutParams();
+        lp.width = recyclerViewWidth;
         updateEmptyStateVisibility();
 
         mController.setOverflowListener(mDataListener);
@@ -172,12 +199,12 @@
                 ? res.getDrawable(R.drawable.bubble_ic_empty_overflow_dark)
                 : res.getDrawable(R.drawable.bubble_ic_empty_overflow_light));
 
-        findViewById(android.R.id.content)
+        findViewById(R.id.bubble_overflow_container)
                 .setBackgroundColor(isNightMode
                         ? res.getColor(R.color.bubbles_dark)
                         : res.getColor(R.color.bubbles_light));
 
-        final TypedArray typedArray = getApplicationContext().obtainStyledAttributes(
+        final TypedArray typedArray = getContext().obtainStyledAttributes(
                 new int[]{android.R.attr.colorBackgroundFloating,
                         android.R.attr.textColorSecondary});
         int bgColor = typedArray.getColor(0, isNightMode ? Color.BLACK : Color.WHITE);
@@ -222,36 +249,6 @@
             }
         }
     };
-
-    @Override
-    public void onStart() {
-        super.onStart();
-    }
-
-    @Override
-    public void onRestart() {
-        super.onRestart();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateOverflow();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-    }
-
-    public void onDestroy() {
-        super.onDestroy();
-    }
 }
 
 class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index e21ba63..0e7e92d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -138,7 +138,7 @@
 
                 info.expandedView = (BubbleExpandedView) inflater.inflate(
                         R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
-                info.expandedView.initialize(controller, stackView);
+                info.expandedView.initialize(controller, stackView, false /* isOverflow */);
             }
 
             if (b.getShortcutInfo() != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
index b347329..a1b0dbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.PointF
 import android.os.Handler
+import android.os.Looper
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import android.view.View
@@ -90,7 +91,7 @@
     private var touchSlop: Int = -1
     private var movedEnough = false
 
-    private val handler = Handler()
+    private val handler = Handler(Looper.myLooper()!!)
     private var performedLongClick = false
 
     @Suppress("UNCHECKED_CAST")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
index b4d7387..9f6dd1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
@@ -460,9 +460,9 @@
 
     /** Plays the given vibration effect if haptics are enabled. */
     @SuppressLint("MissingPermission")
-    private fun vibrateIfEnabled(effect: Int) {
+    private fun vibrateIfEnabled(effectId: Int) {
         if (hapticsEnabled && systemHapticsEnabled) {
-            vibrator.vibrate(effect.toLong())
+            vibrator.vibrate(VibrationEffect.createPredefined(effectId))
         }
     }
 
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 e706f76..801ee2b 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
@@ -59,7 +59,6 @@
 import android.window.WindowContainerTransactionCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.os.SomeArgs;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -505,8 +504,6 @@
             mOnDisplayIdChangeCallback.accept(info.displayId);
         }
 
-        mPipMenuController.attach(leash);
-
         if (mInSwipePipToHomeTransition) {
             final Rect destinationBounds = mPipBoundsState.getBounds();
             // animation is finished in the Launcher and here we directly apply the final touch.
@@ -535,6 +532,7 @@
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
 
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+            mPipMenuController.attach(mLeash);
             final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams,
                     currentBounds);
             scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
@@ -587,6 +585,9 @@
     }
 
     private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
+        // 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);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index bdac37a..5c1d18e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -142,6 +142,15 @@
         mTaskOrganizer.applyTransaction(wct);
     }
 
+    private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
+        if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            // Make the stages adjacent to each other so they occlude what's behind them.
+            wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+            mTaskOrganizer.applyTransaction(wct);
+        }
+    }
+
     private void onStageRootTaskVanished(StageListenerImpl stageListener) {
         if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -357,6 +366,7 @@
         @Override
         public void onRootTaskAppeared() {
             mHasRootTask = true;
+            StageCoordinator.this.onStageRootTaskAppeared(this);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 30f2701..efd42ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -70,9 +70,9 @@
     @CallSuper
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
         if (!taskInfo.hasParentTask()) {
-            mCallbacks.onRootTaskAppeared();
             mRootLeash = leash;
             mRootTaskInfo = taskInfo;
+            mCallbacks.onRootTaskAppeared();
         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
             mChildrenLeashes.put(taskInfo.taskId, leash);
             mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index afae502..65721cc 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -78,21 +78,21 @@
 
     @nullable List<GnssAntennaInfo> getGnssAntennaInfos();
 
-    void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, String attributionTag);
+    void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag);
     void unregisterGnssStatusCallback(in IGnssStatusListener callback);
 
-    void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, String attributionTag);
+    void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag);
     void unregisterGnssNmeaCallback(in IGnssNmeaListener callback);
 
-    void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, String attributionTag);
+    void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections);
 
-    void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, String attributionTag);
+    void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
     int getGnssBatchSize();
-    void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
+    void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId);
     void flushGnssBatch();
     void stopGnssBatch();
 
@@ -101,7 +101,7 @@
     List<String> getProviders(in Criteria criteria, boolean enabledOnly);
     String getBestProvider(in Criteria criteria, boolean enabledOnly);
     ProviderProperties getProviderProperties(String provider);
-    boolean isProviderPackage(String provider, String packageName);
+    boolean isProviderPackage(@nullable String provider, String packageName, @nullable String attributionTag);
     List<String> getProviderPackages(String provider);
 
     void setExtraLocationControllerPackage(String packageName);
@@ -113,10 +113,10 @@
     boolean isLocationEnabledForUser(int userId);
     void setLocationEnabledForUser(boolean enabled, int userId);
 
-    void addTestProvider(String name, in ProviderProperties properties, String packageName, String attributionTag);
-    void removeTestProvider(String provider, String packageName, String attributionTag);
-    void setTestProviderLocation(String provider, in Location location, String packageName, String attributionTag);
-    void setTestProviderEnabled(String provider, boolean enabled, String packageName, String attributionTag);
+    void addTestProvider(String name, in ProviderProperties properties, String packageName, @nullable String attributionTag);
+    void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
+    void setTestProviderLocation(String provider, in Location location, String packageName, @nullable String attributionTag);
+    void setTestProviderEnabled(String provider, boolean enabled, String packageName, @nullable String attributionTag);
 
     LocationTime getGnssTimeMillis();
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 0ce1ad0..96ec590 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -72,6 +72,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -1840,17 +1841,13 @@
      * otherwise.
      *
      * @hide
-     * @deprecated Prefer {@link #isProviderPackage(String, String)} instead.
+     * @deprecated Prefer {@link #isProviderPackage(String, String, String)} instead.
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     public boolean isProviderPackage(@NonNull String packageName) {
-        try {
-            return mService.isProviderPackage(null, packageName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return isProviderPackage(null, packageName, null);
     }
 
     /**
@@ -1861,13 +1858,37 @@
      * @param provider a provider listed by {@link #getAllProviders()} or null
      * @param packageName the package name to test if it is a provider
      * @return true if the given arguments correspond to a provider
+     *
+     * @deprecated Use {@link #isProviderPackage(String, String, String)} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+    public boolean isProviderPackage(@Nullable String provider, @NonNull String packageName) {
+        return isProviderPackage(provider, packageName, null);
+    }
+
+    /**
+     * Returns true if the given provider corresponds to the given package name. If the given
+     * provider is null, this will return true if any provider corresponds to the given package
+     * name and/or attribution tag. If attribution tag is non-null, the provider identity must match
+     * both the given package name and attribution tag.
+     *
+     * @param provider a provider listed by {@link #getAllProviders()} or null
+     * @param packageName the package name to test if it is a provider
+     * @param attributionTag an optional attribution tag within the given package
+     * @return true if the given arguments correspond to a provider
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
-    public boolean isProviderPackage(@Nullable String provider, @NonNull String packageName) {
+    public boolean isProviderPackage(@Nullable String provider, @NonNull String packageName,
+            @Nullable String attributionTag) {
         try {
-            return mService.isProviderPackage(provider, packageName);
+            return mService.isProviderPackage(provider, Objects.requireNonNull(packageName),
+                    attributionTag);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1878,7 +1899,7 @@
      * and an empty list if no package is associated with the provider.
      *
      * @hide
-     * @deprecated Prefer {@link #isProviderPackage(String, String)} instead.
+     * @deprecated Prefer {@link #isProviderPackage(String, String, String)} instead.
      */
     @TestApi
     @Deprecated
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index 9dbdede..0bb7dbb 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -201,11 +201,15 @@
             return false;
         }
         CallerIdentity that = (CallerIdentity) o;
-        return mUid == that.mUid
+        return equalsIgnoringListenerId(that) && Objects.equals(mListenerId, that.mListenerId);
+    }
+
+    public boolean equalsIgnoringListenerId(CallerIdentity that) {
+        return that != null
+                && mUid == that.mUid
                 && mPid == that.mPid
                 && mPackageName.equals(that.mPackageName)
-                && Objects.equals(mAttributionTag, that.mAttributionTag)
-                && Objects.equals(mListenerId, that.mListenerId);
+                && Objects.equals(mAttributionTag, that.mAttributionTag);
     }
 
     @Override
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 13bd856..2703ee3 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1993,6 +1993,33 @@
         return signRSANative(this, sessionId, algorithm, wrappedKey, message);
     }
 
+    /**
+     * Query if the crypto scheme requires the use of a secure decoder
+     * to decode data of the given mime type at the default security level.
+     * The default security level is defined as the highest security level
+     * supported on the device.
+     *
+     * @param mime The mime type of the media data
+     */
+    public boolean requiresSecureDecoder(@NonNull String mime) {
+        return requiresSecureDecoder(mime, getMaxSecurityLevel());
+    }
+
+    /**
+     * Query if the crypto scheme requires the use of a secure decoder
+     * to decode data of the given mime type at the given security level.
+     *
+     * @param mime The mime type of the media data
+     * @param level a security level between {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO}
+     *              and {@link #SECURITY_LEVEL_HW_SECURE_ALL}. Otherwise the special value
+     *              {@link #getMaxSecurityLevel()} is also permitted;
+     *              use {@link #getMaxSecurityLevel()} to indicate the maximum security level
+     *              supported by the device.
+     * @throws IllegalArgumentException if the requested security level is none of the documented
+     * values for the parameter {@code level}.
+     */
+    public native boolean requiresSecureDecoder(@NonNull String mime, @SecurityLevel int level);
+
     @Override
     protected void finalize() throws Throwable {
         try {
diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java
index 1249e0d..0bedbd3 100644
--- a/media/java/android/media/tv/TvInputHardwareInfo.java
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -190,13 +190,16 @@
 
     /** @hide */
     public Builder toBuilder() {
-        return new Builder()
+        Builder newBuilder = new Builder()
             .deviceId(mDeviceId)
             .type(mType)
             .audioType(mAudioType)
             .audioAddress(mAudioAddress)
-            .hdmiPortId(mHdmiPortId)
             .cableConnectionStatus(mCableConnectionStatus);
+        if (mType == TV_INPUT_TYPE_HDMI) {
+            newBuilder.hdmiPortId(mHdmiPortId);
+        }
+        return newBuilder;
     }
 
     public static final class Builder {
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 4f27b197..6141b7f 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -769,10 +769,12 @@
 
         try {
             Log.i(TAG, "openFile with transcode support: " + path);
-            // TODO(b/158466651): Pass the |transcode| variable as flag to openFile
-            Bundle bundle = null;
-            if (!transcode) {
-                bundle = new Bundle();
+            Bundle bundle = new Bundle();
+            if (transcode) {
+                bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES,
+                        new ApplicationMediaCapabilities.Builder().addUnsupportedVideoMimeType(
+                                MediaFormat.MIMETYPE_VIDEO_HEVC).build());
+            } else {
                 bundle.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES,
                         new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
                                 MediaFormat.MIMETYPE_VIDEO_HEVC).build());
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index f38a29c..babb16b 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1953,6 +1953,30 @@
     return VectorToJByteArray(env, signature);
 }
 
+static jboolean android_media_MediaDrm_requiresSecureDecoder(
+        JNIEnv *env, jobject thiz, jstring jmimeType,
+        jint jSecurityLevel) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+    if (!CheckDrm(env, drm)) {
+        return JNI_FALSE;
+    }
+
+    String8 mimeType;
+    if (jmimeType != NULL) {
+        mimeType = JStringToString8(env, jmimeType);
+    }
+
+    DrmPlugin::SecurityLevel securityLevel = jintToSecurityLevel(jSecurityLevel);
+    if (securityLevel == DrmPlugin::kSecurityLevelUnknown) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
+        return JNI_FALSE;
+    }
+
+    if (securityLevel == DrmPlugin::kSecurityLevelMax) {
+        return drm->requiresSecureDecoder(mimeType.c_str());
+    }
+    return drm->requiresSecureDecoder(mimeType.c_str(), securityLevel);
+}
 
 static const JNINativeMethod gMethods[] = {
     { "native_release", "()V", (void *)android_media_MediaDrm_native_release },
@@ -2075,6 +2099,9 @@
 
     { "getMetricsNative", "()Landroid/os/PersistableBundle;",
       (void *)android_media_MediaDrm_native_getMetrics },
+
+    { "requiresSecureDecoder", "(Ljava/lang/String;I)Z",
+      (void *)android_media_MediaDrm_requiresSecureDecoder },
 };
 
 int register_android_media_Drm(JNIEnv *env) {
diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp
index 7f3916f..77583b8 100644
--- a/media/jni/tuner/LnbClient.cpp
+++ b/media/jni/tuner/LnbClient.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <utils/Log.h>
 
+#include "TunerClient.h"
 #include "LnbClient.h"
 
 using ::android::hardware::tv::tuner::V1_0::Result;
@@ -27,14 +28,13 @@
 
 /////////////// LnbClient ///////////////////////
 
-// TODO: pending aidl interface
-LnbClient::LnbClient() {
-    //mTunerLnb = tunerLnb;
+LnbClient::LnbClient(shared_ptr<ITunerLnb> tunerLnb) {
+    mTunerLnb = tunerLnb;
     mId = -1;
 }
 
 LnbClient::~LnbClient() {
-    //mTunerLnb = NULL;
+    mTunerLnb = NULL;
     mLnb = NULL;
     mId = -1;
 }
@@ -45,19 +45,21 @@
 }
 
 Result LnbClient::setCallback(sp<LnbClientCallback> cb) {
-    // TODO: pending aidl interface
-    /*if (mTunerFrontend != NULL) {
+    if (mTunerLnb != NULL) {
         mAidlCallback = ::ndk::SharedRefBase::make<TunerLnbCallback>(cb);
-        mTunerLnb->setCallback(mAidlCallback);
-        return Result::SUCCESS;
-    }*/
+        Status s = mTunerLnb->setCallback(mAidlCallback);
+        return TunerClient::getServiceSpecificErrorCode(s);
+    }
 
     mHidlCallback = new HidlLnbCallback(cb);
     return mLnb->setCallback(mHidlCallback);
 }
 
 Result LnbClient::setVoltage(LnbVoltage voltage) {
-    // TODO: pending aidl interface
+    if (mTunerLnb != NULL) {
+        Status s = mTunerLnb->setVoltage((int)voltage);
+        return TunerClient::getServiceSpecificErrorCode(s);
+    }
 
     if (mLnb != NULL) {
         return mLnb->setVoltage(voltage);
@@ -67,7 +69,10 @@
 }
 
 Result LnbClient::setTone(LnbTone tone) {
-    // TODO: pending aidl interface
+    if (mTunerLnb != NULL) {
+        Status s = mTunerLnb->setTone((int)tone);
+        return TunerClient::getServiceSpecificErrorCode(s);
+    }
 
     if (mLnb != NULL) {
         return mLnb->setTone(tone);
@@ -77,7 +82,10 @@
 }
 
 Result LnbClient::setSatellitePosition(LnbPosition position) {
-    // TODO: pending aidl interface
+    if (mTunerLnb != NULL) {
+        Status s = mTunerLnb->setSatellitePosition((int)position);
+        return TunerClient::getServiceSpecificErrorCode(s);
+    }
 
     if (mLnb != NULL) {
         return mLnb->setSatellitePosition(position);
@@ -87,7 +95,10 @@
 }
 
 Result LnbClient::sendDiseqcMessage(vector<uint8_t> diseqcMessage) {
-    // TODO: pending aidl interface
+    if (mTunerLnb != NULL) {
+        Status s = mTunerLnb->sendDiseqcMessage(diseqcMessage);
+        return TunerClient::getServiceSpecificErrorCode(s);
+    }
 
     if (mLnb != NULL) {
         return mLnb->sendDiseqcMessage(diseqcMessage);
@@ -97,7 +108,10 @@
 }
 
 Result LnbClient::close() {
-    // TODO: pending aidl interface
+    if (mTunerLnb != NULL) {
+        Status s = mTunerLnb->close();
+        return TunerClient::getServiceSpecificErrorCode(s);
+    }
 
     if (mLnb != NULL) {
         return mLnb->close();
@@ -125,6 +139,25 @@
     return Void();
 }
 
-/////////////// LnbClient Helper Methods ///////////////////////
+/////////////// TunerLnbCallback ///////////////////////
 
+TunerLnbCallback::TunerLnbCallback(sp<LnbClientCallback> lnbClientCallback)
+        : mLnbClientCallback(lnbClientCallback) {}
+
+Status TunerLnbCallback::onEvent(int lnbEventType) {
+    if (mLnbClientCallback != NULL) {
+        mLnbClientCallback->onEvent(static_cast<LnbEventType>(lnbEventType));
+        return Status::ok();
+    }
+    return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
+}
+
+Status TunerLnbCallback::onDiseqcMessage(const vector<uint8_t>& diseqcMessage) {
+    if (mLnbClientCallback != NULL) {
+        hidl_vec<uint8_t> msg(begin(diseqcMessage), end(diseqcMessage));
+        mLnbClientCallback->onDiseqcMessage(msg);
+        return Status::ok();
+    }
+    return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
+}
 }  // namespace android
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index 533a996..2465120 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -17,14 +17,18 @@
 #ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_H_
 #define _ANDROID_MEDIA_TV_LNB_CLIENT_H_
 
-//#include <aidl/android/media/tv/tuner/ITunerLnb.h>
+#include <aidl/android/media/tv/tuner/BnTunerLnbCallback.h>
+#include <aidl/android/media/tv/tuner/ITunerLnb.h>
 #include <android/hardware/tv/tuner/1.0/ILnb.h>
 #include <android/hardware/tv/tuner/1.0/ILnbCallback.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
 #include "LnbClientCallback.h"
 
-//using ::aidl::android::media::tv::tuner::ITunerLnb;
+using Status = ::ndk::ScopedAStatus;
+
+using ::aidl::android::media::tv::tuner::BnTunerLnbCallback;
+using ::aidl::android::media::tv::tuner::ITunerLnb;
 
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -42,17 +46,17 @@
 namespace android {
 
 // TODO: pending aidl interface
-/*class TunerLnbCallback : public BnTunerLnbCallback {
+class TunerLnbCallback : public BnTunerLnbCallback {
 
 public:
     TunerLnbCallback(sp<LnbClientCallback> lnbClientCallback);
 
     Status onEvent(int lnbEventType);
-    Status onDiseqcMessage(vector<uint8_t> diseqcMessage);
+    Status onDiseqcMessage(const vector<uint8_t>& diseqcMessage);
 
 private:
     sp<LnbClientCallback> mLnbClientCallback;
-};*/
+};
 
 struct HidlLnbCallback : public ILnbCallback {
 
@@ -68,8 +72,7 @@
 struct LnbClient : public RefBase {
 
 public:
-    // TODO: add TunerLnb as parameter.
-    LnbClient();
+    LnbClient(shared_ptr<ITunerLnb> tunerLnb);
     ~LnbClient();
 
     // TODO: remove after migration to Tuner Service is done.
@@ -114,8 +117,7 @@
      * An AIDL Tuner Lnb Singleton assigned at the first time the Tuner Client
      * opens an Lnb. Default null when lnb is not opened.
      */
-    // TODO: pending on aidl interface
-    //shared_ptr<ITunerLnb> mTunerLnb;
+    shared_ptr<ITunerLnb> mTunerLnb;
 
     /**
      * A Lnb HAL interface that is ready before migrating to the TunerLnb.
@@ -124,7 +126,7 @@
      */
     sp<ILnb> mLnb;
 
-    //shared_ptr<TunerLnbCallback> mAidlCallback;
+    shared_ptr<TunerLnbCallback> mAidlCallback;
     sp<HidlLnbCallback> mHidlCallback;
 
     LnbId mId;
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index f5e3524..498ba0e 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -229,15 +229,15 @@
 sp<LnbClient> TunerClient::openLnb(int lnbHandle) {
     if (mTunerService != NULL) {
         // TODO: handle error code
-        /*shared_ptr<ITunerLnb> tunerLnb;
-        mTunerService->openLnb(demuxHandle, &tunerLnb);
-        return new LnbClient(tunerLnb);*/
+        shared_ptr<ITunerLnb> tunerLnb;
+        mTunerService->openLnb(lnbHandle, &tunerLnb);
+        return new LnbClient(tunerLnb);
     }
 
     if (mTuner != NULL) {
         int id = getResourceIdFromHandle(lnbHandle, LNB);
         // TODO: pending aidl interface
-        sp<LnbClient> lnbClient = new LnbClient();
+        sp<LnbClient> lnbClient = new LnbClient(NULL);
         sp<ILnb> hidlLnb = openHidlLnbById(id);
         if (hidlLnb != NULL) {
             lnbClient->setHidlLnb(hidlLnb);
@@ -252,14 +252,14 @@
 sp<LnbClient> TunerClient::openLnbByName(string lnbName) {
     if (mTunerService != NULL) {
         // TODO: handle error code
-        /*shared_ptr<ITunerLnb> tunerLnb;
+        shared_ptr<ITunerLnb> tunerLnb;
         mTunerService->openLnbByName(lnbName, &tunerLnb);
-        return new LnbClient(tunerLnb);*/
+        return new LnbClient(tunerLnb);
     }
 
     if (mTuner != NULL) {
         // TODO: pending aidl interface
-        sp<LnbClient> lnbClient = new LnbClient();
+        sp<LnbClient> lnbClient = new LnbClient(NULL);
         LnbId id;
         sp<ILnb> hidlLnb = openHidlLnbByName(lnbName, id);
         if (hidlLnb != NULL) {
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 8a1181a..733ee6b 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -20,6 +20,7 @@
 #include <aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.h>
 #include <aidl/android/media/tv/tuner/ITunerService.h>
 #include <aidl/android/media/tv/tuner/TunerFrontendInfo.h>
+#include <android/binder_parcel_utils.h>
 #include <android/hardware/tv/tuner/1.1/ITuner.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
@@ -28,6 +29,8 @@
 #include "DescramblerClient.h"
 #include "LnbClient.h"
 
+using Status = ::ndk::ScopedAStatus;
+
 using ::aidl::android::media::tv::tuner::ITunerService;
 using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
 using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
@@ -132,6 +135,15 @@
      */
     int getHalTunerVersion() { return mTunerVersion; }
 
+    static Result getServiceSpecificErrorCode(Status& s) {
+        if (s.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+            return static_cast<Result>(s.getServiceSpecificError());
+        } else if (s.isOk()) {
+            return Result::SUCCESS;
+        }
+        return Result::UNKNOWN_ERROR;
+    }
+
 private:
     sp<ITuner> getHidlTuner();
     sp<IFrontend> openHidlFrontendById(int id);
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 3751564..d464587 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -74,9 +74,9 @@
     unversioned_until: "current",
 }
 
-cc_fuzz {
-    name: "imagedecoder_fuzzer",
-    srcs: ["fuzz_imagedecoder.cpp"],
+cc_defaults {
+    name: "imagedecoder_fuzzer_defaults",
+    srcs: ["fuzz/fuzz_imagedecoder.cpp"],
     header_libs: ["jni_headers"],
     shared_libs: [
         "libbinder",
@@ -97,6 +97,22 @@
             "allocator_may_return_null = 1",
         ],
     },
-    corpus: ["corpus/*"],
     host_supported: true,
 }
+
+cc_fuzz {
+    name: "imagedecoder_fuzzer",
+    defaults: ["imagedecoder_fuzzer_defaults"],
+    corpus: ["fuzz/corpus/*"],
+}
+
+cc_fuzz {
+    name: "imagedecoder_png_fuzzer",
+    defaults: ["imagedecoder_fuzzer_defaults"],
+    shared_libs: [
+        "libz",
+    ],
+    cflags: [
+        "-DPNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR",
+    ],
+}
diff --git a/native/graphics/jni/corpus/png_test.png b/native/graphics/jni/corpus/png_test.png
deleted file mode 100644
index 5230051..0000000
--- a/native/graphics/jni/corpus/png_test.png
+++ /dev/null
Binary files differ
diff --git a/native/graphics/jni/corpus/baseline_jpeg.jpg b/native/graphics/jni/fuzz/corpus/baseline_jpeg.jpg
similarity index 100%
rename from native/graphics/jni/corpus/baseline_jpeg.jpg
rename to native/graphics/jni/fuzz/corpus/baseline_jpeg.jpg
Binary files differ
diff --git a/native/graphics/jni/corpus/color_wheel.ico b/native/graphics/jni/fuzz/corpus/color_wheel.ico
similarity index 100%
rename from native/graphics/jni/corpus/color_wheel.ico
rename to native/graphics/jni/fuzz/corpus/color_wheel.ico
Binary files differ
diff --git a/native/graphics/jni/corpus/gif_test.gif b/native/graphics/jni/fuzz/corpus/gif_test.gif
similarity index 100%
rename from native/graphics/jni/corpus/gif_test.gif
rename to native/graphics/jni/fuzz/corpus/gif_test.gif
Binary files differ
diff --git a/native/graphics/jni/corpus/google_chrome.ico b/native/graphics/jni/fuzz/corpus/google_chrome.ico
similarity index 100%
rename from native/graphics/jni/corpus/google_chrome.ico
rename to native/graphics/jni/fuzz/corpus/google_chrome.ico
Binary files differ
diff --git a/native/graphics/jni/corpus/heifwriter_input.heic b/native/graphics/jni/fuzz/corpus/heifwriter_input.heic
similarity index 100%
rename from native/graphics/jni/corpus/heifwriter_input.heic
rename to native/graphics/jni/fuzz/corpus/heifwriter_input.heic
Binary files differ
diff --git a/native/graphics/jni/corpus/mandrill.wbmp b/native/graphics/jni/fuzz/corpus/mandrill.wbmp
similarity index 100%
rename from native/graphics/jni/corpus/mandrill.wbmp
rename to native/graphics/jni/fuzz/corpus/mandrill.wbmp
Binary files differ
diff --git a/native/graphics/jni/corpus/progressive_jpeg.jpg b/native/graphics/jni/fuzz/corpus/progressive_jpeg.jpg
similarity index 100%
rename from native/graphics/jni/corpus/progressive_jpeg.jpg
rename to native/graphics/jni/fuzz/corpus/progressive_jpeg.jpg
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_1mp.dng b/native/graphics/jni/fuzz/corpus/sample_1mp.dng
similarity index 100%
rename from native/graphics/jni/corpus/sample_1mp.dng
rename to native/graphics/jni/fuzz/corpus/sample_1mp.dng
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_arw.arw b/native/graphics/jni/fuzz/corpus/sample_arw.arw
similarity index 100%
rename from native/graphics/jni/corpus/sample_arw.arw
rename to native/graphics/jni/fuzz/corpus/sample_arw.arw
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_cr2.cr2 b/native/graphics/jni/fuzz/corpus/sample_cr2.cr2
similarity index 100%
rename from native/graphics/jni/corpus/sample_cr2.cr2
rename to native/graphics/jni/fuzz/corpus/sample_cr2.cr2
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_nef.nef b/native/graphics/jni/fuzz/corpus/sample_nef.nef
similarity index 100%
rename from native/graphics/jni/corpus/sample_nef.nef
rename to native/graphics/jni/fuzz/corpus/sample_nef.nef
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_nrw.nrw b/native/graphics/jni/fuzz/corpus/sample_nrw.nrw
similarity index 100%
rename from native/graphics/jni/corpus/sample_nrw.nrw
rename to native/graphics/jni/fuzz/corpus/sample_nrw.nrw
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_orf.orf b/native/graphics/jni/fuzz/corpus/sample_orf.orf
similarity index 100%
rename from native/graphics/jni/corpus/sample_orf.orf
rename to native/graphics/jni/fuzz/corpus/sample_orf.orf
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_pef.pef b/native/graphics/jni/fuzz/corpus/sample_pef.pef
similarity index 100%
rename from native/graphics/jni/corpus/sample_pef.pef
rename to native/graphics/jni/fuzz/corpus/sample_pef.pef
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_raf.raf b/native/graphics/jni/fuzz/corpus/sample_raf.raf
similarity index 100%
rename from native/graphics/jni/corpus/sample_raf.raf
rename to native/graphics/jni/fuzz/corpus/sample_raf.raf
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_rw2.rw2 b/native/graphics/jni/fuzz/corpus/sample_rw2.rw2
similarity index 100%
rename from native/graphics/jni/corpus/sample_rw2.rw2
rename to native/graphics/jni/fuzz/corpus/sample_rw2.rw2
Binary files differ
diff --git a/native/graphics/jni/corpus/sample_srw.srw b/native/graphics/jni/fuzz/corpus/sample_srw.srw
similarity index 100%
rename from native/graphics/jni/corpus/sample_srw.srw
rename to native/graphics/jni/fuzz/corpus/sample_srw.srw
Binary files differ
diff --git a/native/graphics/jni/corpus/webp-color-profile-lossless.webp b/native/graphics/jni/fuzz/corpus/webp-color-profile-lossless.webp
similarity index 100%
rename from native/graphics/jni/corpus/webp-color-profile-lossless.webp
rename to native/graphics/jni/fuzz/corpus/webp-color-profile-lossless.webp
Binary files differ
diff --git a/native/graphics/jni/corpus/webp_animated.webp b/native/graphics/jni/fuzz/corpus/webp_animated.webp
similarity index 100%
rename from native/graphics/jni/corpus/webp_animated.webp
rename to native/graphics/jni/fuzz/corpus/webp_animated.webp
Binary files differ
diff --git a/native/graphics/jni/corpus/webp_test.webp b/native/graphics/jni/fuzz/corpus/webp_test.webp
similarity index 100%
rename from native/graphics/jni/corpus/webp_test.webp
rename to native/graphics/jni/fuzz/corpus/webp_test.webp
Binary files differ
diff --git a/native/graphics/jni/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
similarity index 96%
rename from native/graphics/jni/fuzz_imagedecoder.cpp
rename to native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
index 015aca7..838bf3f 100644
--- a/native/graphics/jni/fuzz_imagedecoder.cpp
+++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
@@ -22,6 +22,10 @@
 #include <cstdlib>
 #include <memory>
 
+#ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
+#include <fuzz/png_mutator.h>
+#endif
+
 struct DecoderDeleter {
     void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); }
 };
diff --git a/native/graphics/jni/fuzz/png_mutator.h b/native/graphics/jni/fuzz/png_mutator.h
new file mode 100644
index 0000000..f23c343
--- /dev/null
+++ b/native/graphics/jni/fuzz/png_mutator.h
@@ -0,0 +1,326 @@
+// Copyright 2019 Google Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <cstring>
+#include <cassert>
+#include <random>
+
+#include <zlib.h>
+
+// A simple class for parsing, serializing, and mutating an PNG file.
+// https://en.wikipedia.org/wiki/Portable_Network_Graphics
+// It is an example of a custom mutator for libFuzzer
+// (https://llvm.org/docs/LibFuzzer.html) used for
+// "structure-aware coverage-guided fuzzing".
+//
+// If you have a non structure-aware fuzz target for any API that handles
+// PNG inputs, you can turn that fuzz target into a structure-aware one
+// by defining PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR and then
+// including this file.
+class PngMutator {
+  using V = std::vector<uint8_t>;
+
+ public:
+
+  // Parse the input stream as a PNG file,
+  // put every chunk into its own vector,
+  // uncompress chunk data when needed,
+  // merge the IDAT chunks into one vector.
+  PngMutator(std::istream &in) {
+    ihdr_.resize(13);
+    Read4(in);
+    Read4(in); // Skip the 8-byte magic value.
+    // read IHDR.
+    if (ReadInteger(in) != 13) return;
+    if (Read4(in) != Type("IHDR")) return;
+    // Read 13 values.
+    in.read((char*)ihdr_.data(), ihdr_.size());
+    Read4(in);  // ignore CRC
+    ssize_t idat_idx = -1;
+
+    while (in) {
+      uint32_t len = ReadInteger(in);
+      uint32_t type = Read4(in);
+      if (type == Type("IEND")) break;  // do nothing
+      char chunk_name[5];
+      memcpy(chunk_name, &type, 4);
+      chunk_name[4] = 0;
+      if (len > (1 << 20)) return;
+      V v(len);
+      in.read((char *)v.data(), len);
+      Read4(in);  // ignore CRC
+
+      if (type == Type("IDAT")) {
+        if (idat_idx != -1)
+          Append(&chunks_[idat_idx].v, v);
+        else {
+          idat_idx = chunks_.size();
+          chunks_.push_back({type, v});
+        }
+      } else if (type == Type("iCCP")) {
+        auto it = v.begin();
+        while (it < v.end() && isprint(*it)) it++;
+        if (it < v.end() && !*it) it++;
+        if (it < v.end() && !*it) it++;
+        v = V(it, v.end());
+        auto uncompressed = Uncompress(v);
+        chunks_.push_back({type, uncompressed});
+        auto compressed = Compress(uncompressed);
+      } else {
+        chunks_.push_back({type, v});
+      }
+      //  std::cerr << "CHUNK: " << chunk_name << std::endl;
+    }
+    if (idat_idx != -1)
+      chunks_[idat_idx].v = Uncompress(chunks_[idat_idx].v);
+  }
+
+  // Write back the PNG file.
+  void Serialize(std::ostream &out) {
+    const unsigned char header[] = {0x89, 0x50, 0x4e, 0x47,
+                                    0x0d, 0x0a, 0x1a, 0x0a};
+    out.write((const char*)header, sizeof(header));
+    WriteChunk(out, "IHDR", ihdr_);
+    for (auto &ch : chunks_) {
+      if (ch.type == Type("iCCP")) {
+        V v;
+        v.push_back('x');  // assuming the iCCP name doesn't matter.
+        v.push_back(0);
+        v.push_back(0);
+        auto compressed = Compress(ch.v);
+        Append(&v, compressed);
+        WriteChunk(out, ch.type, v);
+      } else {
+        WriteChunk(out, ch.type, ch.v);
+      }
+    }
+
+    WriteChunk(out, "IEND", {});
+  }
+
+  // Raw byte array mutator, like that provided by libFuzzer.
+  using Mutator = size_t (*)(uint8_t *Data, size_t Size, size_t MaxSize);
+
+  // Mutate the in-memory representation of a PNG file.
+  // Given the same Seed, the same mutation is performed.
+  void Mutate(Mutator m, unsigned int Seed) {
+    std::minstd_rand rnd(Seed);
+    auto M = [&](V *v) {
+      if (v->empty())
+        v->resize(v->size() + 1 + rnd() % 256);
+      v->resize(m(v->data(), v->size(), v->size()));
+    };
+    switch (rnd() % 6) {
+      // Mutate IHDR.
+      case 0:
+        m(ihdr_.data(), ihdr_.size(), ihdr_.size());
+        break;
+      // Mutate some other chunk.
+      case 1:
+        if (!chunks_.empty()) M(&chunks_[rnd() % chunks_.size()].v);
+        break;
+      // Shuffle the chunks.
+      case 2:
+        std::shuffle(chunks_.begin(), chunks_.end(), rnd);
+        break;
+      // Delete a random chunk.
+      case 3:
+        if (!chunks_.empty())
+         chunks_.erase(chunks_.begin() + rnd() % chunks_.size());
+        break;
+      // Insert a random chunk with one of the known types, or a random type.
+      case 4: {
+        static const char *types[] = {
+            "IATx", "sTER", "hIST", "sPLT", "mkBF", "mkBS", "mkTS", "prVW",
+            "oFFs", "iDOT", "zTXt", "mkBT", "acTL", "iTXt", "sBIT", "tIME",
+            "iCCP", "vpAg", "tRNS", "cHRM", "PLTE", "bKGD", "gAMA", "sRGB",
+            "pHYs", "fdAT", "fcTL", "tEXt", "IDAT",
+            "pCAL", "sCAL", "eXIf",
+            "fUZz", // special chunk for extra fuzzing hints.
+        };
+        static const size_t n_types = sizeof(types) / sizeof(types[0]);
+        uint32_t type =
+            (rnd() % 10 <= 8) ? Type(types[rnd() % n_types]) : (uint32_t)rnd();
+        size_t len = rnd() % 256;
+        if (type == Type("fUZz"))
+          len = 16;
+        V v(len);
+        for (auto &b : v) b = rnd();
+        size_t pos = rnd() % (chunks_.size() + 1);
+        chunks_.insert(chunks_.begin() + pos, {type, v});
+      } break;
+      // Any more interesting mutations with a PNG file?
+      case 5: {
+        auto it = std::find_if(
+            chunks_.begin(), chunks_.end(),
+            [](const Chunk &ch) { return ch.type == Type("fUZz"); });
+        if (it != chunks_.end())
+          m(it->v.data(), it->v.size(), it->v.size());
+      }
+
+    }
+  }
+
+  // Takes a random chunk from p and inserts into *this.
+  void CrossOver(const PngMutator &p, unsigned int Seed) {
+    if (p.chunks_.empty()) return;
+    std::minstd_rand rnd(Seed);
+    size_t idx = rnd() % p.chunks_.size();
+    auto &ch = p.chunks_[idx];
+    size_t pos = rnd() % (chunks_.size() + 1);
+    chunks_.insert(chunks_.begin() + pos, ch);
+  }
+
+ private:
+  void Append(V *to, const V &from) {
+    to->insert(to->end(), from.begin(), from.end());
+  }
+
+  uint32_t Read4(std::istream &in) {
+    uint32_t res = 0;
+    in.read((char *)&res, sizeof(res));
+    return res;
+  }
+  uint32_t ReadInteger(std::istream &in) {
+    return __builtin_bswap32(Read4(in));
+  }
+  static uint32_t Type(const char *tagname) {
+    uint32_t res;
+    assert(strlen(tagname) == 4);
+    memcpy(&res, tagname, 4);
+    return res;
+  }
+
+  void WriteInt(std::ostream &out, uint32_t x) {
+    x = __builtin_bswap32(x);
+    out.write((char *)&x, sizeof(x));
+  }
+
+  // Chunk is written as:
+  //  * 4-byte length
+  //  * 4-byte type
+  //  * the data itself
+  //  * 4-byte crc (of type and data)
+  void WriteChunk(std::ostream &out, const char *type, const V &chunk,
+                  bool compress = false) {
+    V compressed;
+    const V *v = &chunk;
+    if (compress) {
+      compressed = Compress(chunk);
+      v = &compressed;
+    }
+    uint32_t len = v->size();
+    uint32_t crc = crc32(0, (const unsigned char *)type, 4);
+    if (v->size())
+      crc = crc32(crc, (const unsigned char *)v->data(), v->size());
+    WriteInt(out, len);
+    out.write(type, 4);
+    out.write((const char*)v->data(), v->size());
+    WriteInt(out, crc);
+  }
+
+  void WriteChunk(std::ostream &out, uint32_t type, const V &chunk) {
+    char type_s[5];
+    memcpy(type_s, &type, 4);
+    type_s[4] = 0;
+    WriteChunk(out, type_s, chunk);
+  }
+
+  V Uncompress(const V &compressed) {
+    V v;
+    static const size_t kMaxBuffer = 1 << 28;
+    for (size_t sz = compressed.size() * 4; sz < kMaxBuffer; sz *= 2) {
+      v.resize(sz);
+      unsigned long len = sz;
+      auto res =
+          uncompress(v.data(), &len, compressed.data(), compressed.size());
+      if (res == Z_BUF_ERROR) continue;
+      if (res != Z_OK) return {};
+      v.resize(len);
+      break;
+    }
+    return v;
+  }
+
+  V Compress(const V &uncompressed) {
+    V v;
+    static const size_t kMaxBuffer = 1 << 28;
+    for (size_t sz = uncompressed.size(); sz < kMaxBuffer; sz *= 2) {
+      v.resize(sz);
+      unsigned long len = sz;
+      auto res =
+          compress(v.data(), &len, uncompressed.data(), uncompressed.size());
+      if (res == Z_BUF_ERROR) continue;
+      if (res != Z_OK) return {};
+      v.resize(len);
+      break;
+    }
+    return v;
+  }
+
+  void PrintHex(const V &v, size_t max_n) {
+    for (size_t i = 0; i < max_n && i < v.size(); i++) {
+      std::cerr << "0x" << std::hex << (unsigned)v[i] << " " << std::dec;
+    }
+    std::cerr << std::endl;
+  }
+
+  V ihdr_;
+
+  struct Chunk {
+    uint32_t type;
+    V v;
+  };
+  std::vector<Chunk> chunks_;
+};
+
+
+#ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
+
+extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+#if STANDALONE_TARGET
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+  assert(false && "LLVMFuzzerMutate should not be called from StandaloneFuzzTargetMain");
+  return 0;
+}
+#endif
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+                                          size_t MaxSize, unsigned int Seed) {
+  std::string s(reinterpret_cast<const char*>(Data), Size);
+  std::stringstream in(s);
+  std::stringstream out;
+  PngMutator p(in);
+  p.Mutate(LLVMFuzzerMutate, Seed);
+  p.Serialize(out);
+  const auto &str = out.str();
+  if (str.size() > MaxSize) return Size;
+  memcpy(Data, str.data(), str.size());
+  return str.size();
+}
+
+extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
+                                            const uint8_t *Data2, size_t Size2,
+                                            uint8_t *Out, size_t MaxOutSize,
+                                            unsigned int Seed) {
+  std::stringstream in1(
+      std::string(reinterpret_cast<const char *>(Data1), Size1));
+  std::stringstream in2(
+      std::string(reinterpret_cast<const char *>(Data2), Size2));
+  PngMutator p1(in1);
+  PngMutator p2(in2);
+  p1.CrossOver(p2, Seed);
+  std::stringstream out;
+  p1.Serialize(out);
+  const auto &str = out.str();
+  if (str.size() > MaxOutSize) return 0;
+  memcpy(Out, str.data(), str.size());
+  return str.size();
+}
+
+#endif  // PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt b/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt
index 56f599a..7efaf0b 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoControlsService.kt
@@ -74,6 +74,7 @@
     private val controls = HashMap<String, Control>()
     private val publishers = ArrayList<UglyPublisher>()
     private val rng = Random()
+    private val metricsLogger = MetricsLogger()
 
     private var lastToyIcon: Icon? = null
 
@@ -184,7 +185,6 @@
         return getPendingIntent()
     }
 
-
     override fun performControlAction(
         controlId: String,
         action: ControlAction,
@@ -196,7 +196,7 @@
                 controls[CONTROL_ID_FOOD] = makeFoodBowlControl(true)
                 Log.v(TAG, "Bowl refilled. (Registering job.)")
                 NekoService.registerJob(this, FOOD_SPAWN_CAT_DELAY_MINS)
-                MetricsLogger.histogram(this, "egg_neko_offered_food", 11)
+                metricsLogger.histogram("egg_neko_offered_food", 11)
                 prefs.foodState = 11
             }
             CONTROL_ID_TOY -> {
diff --git a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
index 460fa3a..20527af 100644
--- a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
+++ b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
@@ -47,6 +47,7 @@
     ) : super(context, attrs, defStyle) {
     }
 
+    @Suppress("DEPRECATION")
     override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
         var lp = layoutParams as FrameLayout.LayoutParams?
         if (lp != null && insets != null) {
diff --git a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
index ce439a9..578de01 100644
--- a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
+++ b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
@@ -49,6 +49,7 @@
     private lateinit var label: Button
     private lateinit var grid: GridLayout
 
+    @Suppress("DEPRECATION")
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index 12dc170..05561d7 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
 
     <application
             android:label="@string/app_label"
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index cb55c72..7a239af 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -273,6 +273,7 @@
                             .setLowPower(mRequest.isLowPower())
                             .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
                             .setWorkSource(mRequest.getWorkSource())
+                            .setHiddenFromAppOps(true)
                             .build();
                     mLocationManager.requestLocationUpdates(mProvider, request,
                             mContext.getMainExecutor(), this);
diff --git a/packages/FusedLocation/test/AndroidManifest.xml b/packages/FusedLocation/test/AndroidManifest.xml
index c00d15e..c38cecb 100644
--- a/packages/FusedLocation/test/AndroidManifest.xml
+++ b/packages/FusedLocation/test/AndroidManifest.xml
@@ -9,6 +9,7 @@
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
 
     <application android:label="FusedLocation Tests">
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 3105e98..711a22f 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -576,8 +576,7 @@
     <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
     <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
-    <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
-    <skip />
+    <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
     <string name="cell_data_off_content_description" msgid="2280700839891636498">"モバイルデータ OFF"</string>
     <string name="not_default_data_content_description" msgid="6517068332106592887">"データを使用するように設定されていません"</string>
     <string name="accessibility_no_phone" msgid="2687419663127582503">"電波状態:なし"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index f5be76e..72ddc8f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -576,8 +576,7 @@
     <string name="data_connection_4g_plus" msgid="5194902328408751020">"४G+"</string>
     <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
-    <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
-    <skip />
+    <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
     <string name="cell_data_off_content_description" msgid="2280700839891636498">"मोबाइल डेटा बंद आहे"</string>
     <string name="not_default_data_content_description" msgid="6517068332106592887">"डेटा वापरण्यासाठी सेट केलेले नाही"</string>
     <string name="accessibility_no_phone" msgid="2687419663127582503">"कोणताही फोन नाही."</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 2ae7d71..e04e201 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -576,8 +576,7 @@
     <string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
     <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
     <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
-    <!-- no translation found for data_connection_carrier_wifi (2250268321065848954) -->
-    <skip />
+    <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
     <string name="cell_data_off_content_description" msgid="2280700839891636498">"ਮੋਬਾਈਲ ਡਾਟਾ ਬੰਦ"</string>
     <string name="not_default_data_content_description" msgid="6517068332106592887">"ਡਾਟਾ ਵਰਤਣ ਲਈ ਸੈੱਟ ਨਹੀਂ"</string>
     <string name="accessibility_no_phone" msgid="2687419663127582503">"ਕੋਈ ਫ਼ੋਨ ਨਹੀਂ।"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 897abea..5e880a9 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -140,15 +140,15 @@
     <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ఓపెన్ నెట్‌వర్క్"</string>
     <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"సురక్షిత నెట్‌వర్క్"</string>
     <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
-    <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"తీసివేయబడిన అనువర్తనాలు"</string>
-    <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"తీసివేయబడిన అనువర్తనాలు మరియు వినియోగదారులు"</string>
+    <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"తీసివేయబడిన యాప్‌లు"</string>
+    <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"తీసివేయబడిన యాప్‌లు మరియు వినియోగదారులు"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"సిస్టమ్ అప్‌డేట్‌లు"</string>
     <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB టీథరింగ్"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"పోర్టబుల్ హాట్‌స్పాట్"</string>
     <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"బ్లూటూత్ టెథెరింగ్"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"టీథరింగ్"</string>
     <string name="tether_settings_title_all" msgid="8910259483383010470">"టీథరింగ్ &amp; పోర్టబుల్ హాట్‌స్పాట్"</string>
-    <string name="managed_user_title" msgid="449081789742645723">"అన్ని కార్యాలయ అనువర్తనాలు"</string>
+    <string name="managed_user_title" msgid="449081789742645723">"అన్ని కార్యాలయ యాప్‌లు"</string>
     <string name="user_guest" msgid="6939192779649870792">"అతిథి"</string>
     <string name="unknown" msgid="3544487229740637809">"తెలియదు"</string>
     <string name="running_process_item_user_label" msgid="3988506293099805796">"వినియోగదారు: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
@@ -313,14 +313,14 @@
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్‌ను ఎనేబుల్ చేస్తుంది."</string>
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్‌ను ఎనేబుల్ చేస్తుంది."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string>
-    <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ అనువర్తనాన్ని ప్రారంభించు"</string>
+    <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ యాప్‌ను ప్రారంభించు"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP తనిఖీ"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP తనిఖీ ప్రవర్తనను సెట్ చేయండి"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"డీబగ్గింగ్"</string>
     <string name="debug_app" msgid="8903350241392391766">"డీబగ్ యాప్‌ను ఎంచుకోండి"</string>
     <string name="debug_app_not_set" msgid="1934083001283807188">"డీబగ్ యాప్ సెట్ చేయబడలేదు"</string>
     <string name="debug_app_set" msgid="6599535090477753651">"డీబగ్గింగ్ యాప్: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <string name="select_application" msgid="2543228890535466325">"అనువర్తనాన్ని ఎంచుకోండి"</string>
+    <string name="select_application" msgid="2543228890535466325">"యాప్‌ను ఎంచుకోండి"</string>
     <string name="no_application" msgid="9038334538870247690">"ఏదీ వద్దు"</string>
     <string name="wait_for_debugger" msgid="7461199843335409809">"డీబగ్గర్ కోసం వేచి ఉండండి"</string>
     <string name="wait_for_debugger_summary" msgid="6846330006113363286">"డీబగ్ చేయబడిన యాప్ అమలు కావడానికి ముందు జోడించాల్సిన డీబగ్గర్ కోసం వేచి ఉంటుంది"</string>
@@ -547,7 +547,7 @@
     <string name="user_new_profile_name" msgid="2405500423304678841">"కొత్త ప్రొఫైల్"</string>
     <string name="user_info_settings_title" msgid="6351390762733279907">"వినియోగదారు సమాచారం"</string>
     <string name="profile_info_settings_title" msgid="105699672534365099">"ప్రొఫైల్ సమాచారం"</string>
-    <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్‌ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్‌ను సెటప్ చేయాల్సి ఉంటుంది."</string>
+    <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్‌ను సృష్టించడానికి ముందు, మీ యాప్‌లు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్‌ను సెటప్ చేయాల్సి ఉంటుంది."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"లాక్‌ను సెట్ చేయి"</string>
     <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>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index ac20ee1..927da0a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -267,19 +267,28 @@
             permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
                     packageName, userId);
         }
+
+        boolean permittedByParentAdmin = true;
+        EnforcedAdmin profileAdmin = null;
         int managedProfileId = getManagedProfileId(context, userId);
-        EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context,
-                getUserHandleOf(managedProfileId));
-        boolean permittedByProfileAdmin = true;
-        if (profileAdmin != null) {
-            permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component,
-                    packageName, managedProfileId);
+        if (managedProfileId != UserHandle.USER_NULL) {
+            profileAdmin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId));
+            // If the device is an organization-owned device with a managed profile, the
+            // managedProfileId will be used instead of the affected userId. This is because
+            // isInputMethodPermittedByAdmin is called on the parent DPM instance, which will
+            // return results affecting the personal profile.
+            if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
+                DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm,
+                        UserManager.get(context).getUserInfo(managedProfileId));
+                permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin(
+                        profileAdmin.component, packageName, managedProfileId);
+            }
         }
-        if (!permitted && !permittedByProfileAdmin) {
+        if (!permitted && !permittedByParentAdmin) {
             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
         } else if (!permitted) {
             return admin;
-        } else if (!permittedByProfileAdmin) {
+        } else if (!permittedByParentAdmin) {
             return profileAdmin;
         }
         return null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
index b2e75ea..cd22247 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
@@ -38,14 +38,13 @@
          * @return An [Estimate] object with the latest battery estimates.
          */
         @JvmStatic
+        @Suppress("DEPRECATION")
         fun getCachedEstimateIfAvailable(context: Context): Estimate? {
             // if time > 2 min return null or the estimate otherwise
             val resolver = context.contentResolver
-            val lastUpdateTime = Instant.ofEpochMilli(
-                    Settings.Global.getLong(
-                            resolver, Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME, -1))
+            val lastUpdateTime = getLastCacheUpdateTime(context)
             return if (Duration.between(lastUpdateTime,
-                            Instant.now()).compareTo(Duration.ofMinutes(1)) > 0) {
+                            Instant.now()) > Duration.ofMinutes(1)) {
                 null
             } else Estimate(
                     Settings.Global.getLong(resolver,
@@ -65,6 +64,7 @@
          * @param estimate the [Estimate] object to store
          */
         @JvmStatic
+        @Suppress("DEPRECATION")
         fun storeCachedEstimate(context: Context, estimate: Estimate) {
             // store the estimate and update the timestamp
             val resolver = context.contentResolver
@@ -82,6 +82,7 @@
          * Returns when the estimate was last updated as an Instant
          */
         @JvmStatic
+        @Suppress("DEPRECATION")
         fun getLastCacheUpdateTime(context: Context): Instant {
             return Instant.ofEpochMilli(
                     Settings.Global.getLong(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 859f5a6..1c53418 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -388,6 +388,9 @@
     <!-- Permission required for CTS to test sensor privacy behavior -->
     <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
 
+    <!-- Permission needed for CTS test - CallLogTest -->
+    <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 39fbb34..ca9dcd6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -354,14 +354,6 @@
                 android:permission="android.permission.BIND_WALLPAPER"
                 android:exported="true" />
 
-        <activity
-            android:name="com.android.wm.shell.bubbles.BubbleOverflowActivity"
-            android:theme="@style/BubbleOverflow"
-            android:excludeFromRecents="true"
-            android:documentLaunchMode="always"
-            android:resizeableActivity="true">
-        </activity>
-
         <activity android:name=".tuner.TunerActivity"
                   android:enabled="false"
                   android:icon="@drawable/tuner"
@@ -441,6 +433,15 @@
             android:excludeFromRecents="true">
         </activity>
 
+        <!-- started from SensoryPrivacyService -->
+        <activity android:name=".sensorprivacy.SensorUseStartedActivity"
+                  android:exported="true"
+                  android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
+                  android:theme="@style/Theme.SystemUI.Dialog.Alert"
+                  android:finishOnCloseSystemDialogs="true">
+        </activity>
+
+
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbAccessoryUriActivity"
             android:exported="true"
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 0c8c5c4..dec83d9 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -43,6 +43,7 @@
         <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
         <item>com.android.systemui.toast.ToastUI</item>
         <item>com.android.systemui.wmshell.WMShell</item>
+        <item>com.android.systemui.media.systemsounds.HomeSoundEffectController</item>
     </string-array>
 
     <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ac2e342..6e9c5dc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -974,6 +974,11 @@
     <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] -->
     <string name="quick_settings_screen_record_stop">Stop</string>
 
+    <!--- Content of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_start_use_mic_dialog_content">To continue, &lt;b&gt;<xliff:g id="app" example="Gmail">%s</xliff:g>&lt;/b&gt; needs access to your device microphone.</string>
+    <!--- Content of dialog triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_start_use_camera_dialog_content">To continue, &lt;b&gt;<xliff:g id="app" example="Gmail">%s</xliff:g>&lt;/b&gt; needs access to your device’s camera.</string>
+
     <!-- Default name for the media device shown in the output switcher when the name is not available [CHAR LIMIT=30] -->
     <string name="media_seamless_remote_device">Device</string>
 
@@ -1039,6 +1044,9 @@
     <!-- Message shown when face authentication fails and the pin pad is visible. [CHAR LIMIT=60] -->
     <string name="keyguard_retry">Swipe up to try again</string>
 
+    <!-- Message shown when notifying user to unlock in order to use NFC. [CHAR LIMIT=60] -->
+    <string name="require_unlock_for_nfc">Unlock to use NFC</string>
+
     <!-- Text on keyguard screen and in Quick Settings footer indicating that the user's device belongs to their organization. [CHAR LIMIT=60] -->
     <string name="do_disclosure_generic">This device belongs to your organization</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6c0635a..d706ec2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -15,8 +15,6 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="BubbleOverflow" parent="@android:style/Theme.NoTitleBar"></style>
-
     <style name="ClearAllButtonDefaultMargins">
         <item name="android:layout_marginStart">0dp</item>
         <item name="android:layout_marginTop">0dp</item>
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 57a4dab..388eeb6 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
@@ -24,7 +24,6 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.MotionEvent;
-import android.window.TransitionFilter;
 
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.recents.model.Task;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 77e568c..a40dc7a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -255,6 +255,7 @@
         private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
         private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
         private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+        private final Context mContext;
         private KeyguardClockSwitchController mKeyguardClockSwitchController;
         private View mClock;
         private int mUsableWidth;
@@ -278,6 +279,7 @@
                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
             mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
             setCancelable(false);
+            mContext = context;
         }
 
         @Override
@@ -301,7 +303,7 @@
             mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
             mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
 
-            setContentView(LayoutInflater.from(getContext())
+            setContentView(LayoutInflater.from(mContext)
                     .inflate(R.layout.keyguard_presentation, null));
 
             // Logic to make the lock screen fullscreen
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b43496c..e59769b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -60,6 +60,7 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.nfc.NfcAdapter;
 import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.Handler;
@@ -183,6 +184,7 @@
     private static final int MSG_KEYGUARD_GOING_AWAY = 342;
     private static final int MSG_LOCK_SCREEN_MODE = 343;
     private static final int MSG_TIME_FORMAT_UPDATE = 344;
+    private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
 
     public static final int LOCK_SCREEN_MODE_NORMAL = 0;
     public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1;
@@ -1259,6 +1261,8 @@
             } else if (ACTION_USER_REMOVED.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
                         intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1), 0));
+            } else if (NfcAdapter.ACTION_REQUIRE_UNLOCK_FOR_NFC.equals(action)) {
+                mHandler.sendEmptyMessage(MSG_REQUIRE_NFC_UNLOCK);
             }
         }
     };
@@ -1314,6 +1318,16 @@
         public void onAuthenticationAcquired(int acquireInfo) {
             handleFingerprintAcquired(acquireInfo);
         }
+
+        @Override
+        public void onUdfpsPointerDown(int sensorId) {
+            Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId);
+        }
+
+        @Override
+        public void onUdfpsPointerUp(int sensorId) {
+            Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId);
+        }
     };
 
     private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
@@ -1725,6 +1739,8 @@
                         break;
                     case MSG_TIME_FORMAT_UPDATE:
                         handleTimeFormatUpdate((String) msg.obj);
+                    case MSG_REQUIRE_NFC_UNLOCK:
+                        handleRequireUnlockForNfc();
                         break;
                     default:
                         super.handleMessage(msg);
@@ -1787,6 +1803,7 @@
         allUserFilter.addAction(ACTION_USER_UNLOCKED);
         allUserFilter.addAction(ACTION_USER_STOPPED);
         allUserFilter.addAction(ACTION_USER_REMOVED);
+        allUserFilter.addAction(NfcAdapter.ACTION_REQUIRE_UNLOCK_FOR_NFC);
         mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastAllReceiver, allUserFilter,
                 mHandler, UserHandle.ALL);
 
@@ -2690,6 +2707,19 @@
     }
 
     /**
+     * Handle {@link #MSG_REQUIRE_NFC_UNLOCK}
+     */
+    private void handleRequireUnlockForNfc() {
+        Assert.isMainThread();
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onRequireUnlockForNfc();
+            }
+        }
+    }
+
+    /**
      * Handle {@link #MSG_REPORT_EMERGENCY_CALL_ACTION}
      */
     private void handleReportEmergencyCallAction() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 36617c2..e561a5a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -328,4 +328,9 @@
      */
     public void onLockScreenModeChanged(int mode) { }
 
+    /**
+     * Called when notifying user to unlock in order to use NFC.
+     */
+    public void onRequireUnlockForNfc() { }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 9f6c19b..55359ea 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -27,6 +27,7 @@
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.media.systemsounds.HomeSoundEffectController;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsModule;
@@ -178,4 +179,10 @@
     @IntoMap
     @ClassKey(WMShell.class)
     public abstract SystemUI bindWMShell(WMShell sysui);
+
+    /** Inject into HomeSoundEffectController. */
+    @Binds
+    @IntoMap
+    @ClassKey(HomeSoundEffectController.class)
+    public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
new file mode 100644
index 0000000..dd3d02a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
@@ -0,0 +1,82 @@
+/*
+ * 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.media.systemsounds;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.media.AudioManager;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import javax.inject.Inject;
+
+/**
+ * If a sound effect is defined for {@link android.media.AudioManager#FX_HOME}, a sound is played
+ * when the home task moves to front and the last task that moved to front was not the home task.
+ */
+@SysUISingleton
+public class HomeSoundEffectController extends SystemUI {
+
+    private final AudioManager mAudioManager;
+    private final TaskStackChangeListeners mTaskStackChangeListeners;
+    // Initialize true because home sound should not be played when the system boots.
+    private boolean mIsLastTaskHome = true;
+
+    @Inject
+    public HomeSoundEffectController(
+            Context context,
+            AudioManager audioManager,
+            TaskStackChangeListeners taskStackChangeListeners) {
+        super(context);
+        mAudioManager = audioManager;
+        mTaskStackChangeListeners = taskStackChangeListeners;
+    }
+
+    @Override
+    public void start() {
+        if (mAudioManager.isHomeSoundEffectEnabled()) {
+            mTaskStackChangeListeners.registerTaskStackListener(
+                    new TaskStackChangeListener() {
+                        @Override
+                        public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+                            handleHomeTaskMovedToFront(taskInfo);
+                        }
+                    });
+        }
+    }
+
+    private boolean isHomeTask(ActivityManager.RunningTaskInfo taskInfo) {
+        return taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_HOME;
+    }
+
+    /**
+     * To enable a home sound, check if the home app moves to front.
+     */
+    private void handleHomeTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+        boolean isCurrentTaskHome = isHomeTask(taskInfo);
+        // If the last task is home we don't want to play the home sound. This avoids playing
+        // the home sound after FallbackHome transitions to Home
+        if (!mIsLastTaskHome && isCurrentTaskHome) {
+            mAudioManager.playSoundEffect(AudioManager.FX_HOME);
+        }
+        mIsLastTaskHome = isCurrentTaskHome;
+    }
+}
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 98740a2..b6e07b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -20,7 +20,6 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
-import android.annotation.StringRes;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfig;
@@ -65,7 +64,7 @@
 
     @Override
     public @DrawableRes int getIconRes() {
-        return R.drawable.ic_camera_blocked;
+        return com.android.internal.R.drawable.ic_camera_blocked;
     }
 
     @Override
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 8cc0d7b..9cc6f09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -63,7 +63,7 @@
 
     @Override
     public @DrawableRes int getIconRes() {
-        return R.drawable.ic_mic_blocked;
+        return com.android.internal.R.drawable.ic_mic_blocked;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
new file mode 100644
index 0000000..c1161f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.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.systemui.sensorprivacy
+
+import android.app.AppOpsManager
+import android.content.DialogInterface
+import android.content.Intent.EXTRA_PACKAGE_NAME
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.*
+import android.os.Bundle
+import android.text.Html
+import com.android.internal.app.AlertActivity
+import com.android.systemui.R
+
+/**
+ * Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is
+ * currently in "sensor privacy mode", aka. muted.
+ *
+ * <p>The dialog is started for the user the app is running for which might be a secondary users.
+ */
+class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListener {
+    private var sensor = -1
+    private lateinit var sensorUsePackageName: String
+
+    private lateinit var sensorPrivacyManager: SensorPrivacyManager
+    private lateinit var appOpsManager: AppOpsManager
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setResult(RESULT_CANCELED)
+        sensorPrivacyManager = getSystemService(SensorPrivacyManager::class.java)!!
+        appOpsManager = getSystemService(AppOpsManager::class.java)!!
+
+        sensorUsePackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME) ?: return
+        sensor = intent.getIntExtra(EXTRA_SENSOR, -1).also {
+            if (it == -1) {
+                finish()
+                return
+            }
+        }
+
+        sensorPrivacyManager.addSensorPrivacyListener(sensor) { isBlocked ->
+            if (!isBlocked) {
+                dismiss()
+            }
+        }
+        if (!sensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)) {
+            finish()
+            return
+        }
+
+        mAlertParams.apply {
+            try {
+                mMessage = Html.fromHtml(getString(when (sensor) {
+                    INDIVIDUAL_SENSOR_MICROPHONE ->
+                        R.string.sensor_privacy_start_use_mic_dialog_content
+                    INDIVIDUAL_SENSOR_CAMERA ->
+                        R.string.sensor_privacy_start_use_camera_dialog_content
+                    else -> Resources.ID_NULL
+                }, packageManager.getApplicationInfo(sensorUsePackageName, 0)
+                        .loadLabel(packageManager)), 0)
+            } catch (e: PackageManager.NameNotFoundException) {
+                finish()
+                return
+            }
+
+            mIconId = when (sensor) {
+                INDIVIDUAL_SENSOR_MICROPHONE ->
+                    com.android.internal.R.drawable.perm_group_microphone
+                INDIVIDUAL_SENSOR_CAMERA -> com.android.internal.R.drawable.perm_group_camera
+                else -> Resources.ID_NULL
+            }
+
+            mPositiveButtonText = getString(
+                    com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button)
+            mNegativeButtonText = getString(android.R.string.cancel)
+            mPositiveButtonListener = this@SensorUseStartedActivity
+            mNegativeButtonListener = this@SensorUseStartedActivity
+        }
+
+        setupAlert()
+    }
+
+    override fun onStart() {
+        super.onStart()
+
+        sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true)
+    }
+
+    override fun onClick(dialog: DialogInterface?, which: Int) {
+        when (which) {
+            BUTTON_POSITIVE -> {
+                sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+                setResult(RESULT_OK)
+            }
+        }
+
+        dismiss()
+    }
+
+    override fun onStop() {
+        super.onDestroy()
+
+        sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5259aa9..c816784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -855,5 +855,13 @@
                 updateIndication(false);
             }
         }
+
+        @Override
+        public void onRequireUnlockForNfc() {
+            showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc),
+                    false /* isError */, false /* hideOnScreenOff */);
+            hideTransientIndicationDelayed(HIDE_DELAY_MS);
+        }
+
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b5963ef..a12179c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -78,6 +78,7 @@
     private ExpandableNotificationRow mTrackedHeadsUpRow;
     private float mAppearFraction;
     private boolean mIsShadeOpening;
+    private float mSectionPadding;
 
     /** Tracks the state from AlertingNotificationManager#hasNotifications() */
     private boolean mHasAlertEntries;
@@ -105,6 +106,14 @@
         return mIsShadeOpening;
     }
 
+    void setSectionPadding(float padding) {
+        mSectionPadding = padding;
+    }
+
+    float getSectionPadding() {
+        return mSectionPadding;
+    }
+
     private static int getZDistanceBetweenElements(Context context) {
         return Math.max(1, context.getResources()
                 .getDimensionPixelSize(R.dimen.z_distance_between_notifications));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6abbc6b..f07d874 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -554,6 +554,11 @@
         mAmbientState.setIsShadeOpening(isOpening);
     }
 
+    void setSectionPadding(float margin) {
+        mAmbientState.setSectionPadding(margin);
+        requestChildrenUpdate();
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onFinishInflate() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 5126f55..879dad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -275,6 +275,10 @@
         mView.setIsShadeOpening(isOpening);
     }
 
+    public void setSectionPadding(float padding) {
+        mView.setSectionPadding(padding);
+    }
+
     private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
         @Override
         public void onMenuClicked(
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 2c206b1..e6efba7 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
@@ -341,7 +341,8 @@
         boolean isEmptyShadeView = child instanceof EmptyShadeView;
 
         childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
-        float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
+        float inset = ambientState.getTopPadding() + ambientState.getStackTranslation()
+                + ambientState.getSectionPadding();
         if (i <= algorithmState.getIndexOfExpandingNotification()) {
             inset += ambientState.getExpandAnimationTopChange();
         }
@@ -563,6 +564,10 @@
             childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
         }
         childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
+        if (child instanceof SectionHeaderView) {
+            // Add padding before sections for overscroll effect.
+            childViewState.yTranslation += ambientState.getSectionPadding();
+        }
         if (childViewState.yTranslation >= shelfStart) {
             childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
             childViewState.inShelf = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 6cd7a74..1dfd1f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -32,7 +32,6 @@
  * Utility class to calculate the clock position and top padding of notifications on Keyguard.
  */
 public class KeyguardClockPositionAlgorithm {
-
     /**
      * How much the clock height influences the shade position.
      * 0 means nothing, 1 means move the shade up by the height of the clock
@@ -81,6 +80,11 @@
     private int mMinTopMargin;
 
     /**
+     * Minimum top inset (in pixels) to avoid overlap with any display cutouts.
+     */
+    private int mCutoutTopInset = 0;
+
+    /**
      * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
      * the ambient indication.
      */
@@ -172,7 +176,7 @@
             float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY,
             boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount,
             boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon,
-            float qsExpansion) {
+            float qsExpansion, int cutoutTopInset) {
         mMinTopMargin = statusBarMinHeight + (showLockIcon
                 ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon);
         mMaxShadeBottom = maxShadeBottom;
@@ -188,6 +192,7 @@
         mBypassEnabled = bypassEnabled;
         mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
         mQsExpansion = qsExpansion;
+        mCutoutTopInset = cutoutTopInset;
     }
 
     public void run(Result result) {
@@ -270,10 +275,13 @@
 
         float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount;
 
-        // TODO(b/12836565) - prototyping only adjustment
         if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
-            // This will keep the clock at the top
-            clockYDark = (int) (clockY + burnInPreventionOffsetY());
+            // This will keep the clock at the top but out of the cutout area
+            float shift = 0;
+            if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) {
+                shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock);
+            }
+            clockYDark = clockY + burnInPreventionOffsetY() + shift;
         }
         return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
     }
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 d132abe..3e09784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -50,6 +50,7 @@
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.MathUtils;
+import android.view.DisplayCutout;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -344,6 +345,7 @@
     private float mEmptyDragAmount;
     private float mDownX;
     private float mDownY;
+    private int mDisplayCutoutTopInset = 0; // in pixels
 
     private final KeyguardClockPositionAlgorithm
             mClockPositionAlgorithm =
@@ -475,6 +477,7 @@
     private boolean mShowingKeyguardHeadsUp;
     private boolean mAllowExpandForSmallExpansion;
     private Runnable mExpandAfterLayoutRunnable;
+    private float mSectionPadding;
 
     /**
      * Is this a collapse that started on the panel where we should allow the panel to intercept
@@ -909,7 +912,8 @@
                     hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
                     bypassEnabled, getUnlockedStackScrollerPadding(),
                     mUpdateMonitor.shouldShowLockIcon(),
-                    getQsExpansionFraction());
+                    getQsExpansionFraction(),
+                    mDisplayCutoutTopInset);
             mClockPositionAlgorithm.run(mClockPositionResult);
             mKeyguardStatusViewController.updatePosition(
                     mClockPositionResult.clockX, mClockPositionResult.clockY,
@@ -2413,6 +2417,16 @@
     }
 
     @Override
+    public void setSectionPadding(float padding) {
+        if (padding == mSectionPadding) {
+            return;
+        }
+        mSectionPadding = padding;
+        mQsFrame.setTranslationY(padding);
+        mNotificationStackScrollLayoutController.setSectionPadding(padding);
+    }
+
+    @Override
     protected void setOverExpansion(float overExpansion, boolean isPixels) {
         if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
             return;
@@ -2499,19 +2513,6 @@
     }
 
     @Override
-    protected boolean shouldExpandToTopOfClearAll(float targetHeight) {
-        boolean perform = super.shouldExpandToTopOfClearAll(targetHeight);
-        if (!perform) {
-            return false;
-        }
-        // Let's make sure we're not appearing but the animation will end below the appear.
-        // Otherwise quick settings would jump at the end of the animation.
-        float fraction = mNotificationStackScrollLayoutController
-                .calculateAppearFraction(targetHeight);
-        return fraction >= 1.0f;
-    }
-
-    @Override
     protected boolean shouldUseDismissingAnimation() {
         return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
                 || !isTracking());
@@ -2530,11 +2531,6 @@
     }
 
     @Override
-    protected int getClearAllHeightWithPadding() {
-        return mNotificationStackScrollLayoutController.getFooterViewHeightWithPadding();
-    }
-
-    @Override
     protected boolean isTrackingBlocked() {
         return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
     }
@@ -3835,6 +3831,9 @@
 
     private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
         public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+            final DisplayCutout displayCutout = v.getRootWindowInsets().getDisplayCutout();
+            mDisplayCutoutTopInset = displayCutout != null ? displayCutout.getSafeInsetTop() : 0;
+
             mNavigationBarBottomHeight = insets.getStableInsetBottom();
             updateMaxHeadsUpTranslation();
             return insets;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index a1112dc..bee0cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -57,6 +57,10 @@
         }
     }
 
+    protected boolean isShadeOpening() {
+        return mState == STATE_OPENING;
+    }
+
     @Override
     protected Parcelable onSaveInstanceState() {
         Bundle bundle = new Bundle();
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 da82986..3031b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -32,6 +32,7 @@
 import android.os.SystemClock;
 import android.os.VibrationEffect;
 import android.util.Log;
+import android.util.MathUtils;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -67,6 +68,13 @@
     private static final int INITIAL_OPENING_PEEK_DURATION = 200;
     private static final int PEEK_ANIMATION_DURATION = 360;
     private static final int NO_FIXED_DURATION = -1;
+    private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
+    private static final long SHADE_OPEN_SPRING_BACK_DURATION = 200L;
+    private static final float MIN_OVERSCROLL = -50;
+    private static final float MAX_OVERSCROLL = 30;
+
+    private float mFlingTarget;
+    private float mFlingVelocity;
     protected long mDownTime;
     protected boolean mTouchSlopExceededBeforeDown;
     private float mMinExpandHeight;
@@ -569,14 +577,6 @@
 
     protected void flingToHeight(float vel, boolean expand, float target,
             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
-        // Hack to make the expand transition look nice when clear all button is visible - we make
-        // the animation only to the last notification, and then jump to the maximum panel height so
-        // clear all just fades in and the decelerating motion is towards the last notification.
-        final boolean clearAllExpandHack = expand &&
-                shouldExpandToTopOfClearAll(getMaxPanelHeight() - getClearAllHeightWithPadding());
-        if (clearAllExpandHack) {
-            target = getMaxPanelHeight() - getClearAllHeightWithPadding();
-        }
         if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
             InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
             notifyExpandingFinished();
@@ -584,13 +584,14 @@
         }
         mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
         ValueAnimator animator = createHeightAnimator(target);
+        mFlingTarget = target;
         if (expand) {
             if (expandBecauseOfFalsing && vel < 0) {
                 vel = 0;
             }
             mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, mView.getHeight());
             if (vel == 0) {
-                animator.setDuration(350);
+                animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
             }
         } else {
             if (shouldUseDismissingAnimation()) {
@@ -615,6 +616,7 @@
                 animator.setDuration(mFixedDuration);
             }
         }
+        mFlingVelocity = vel;
         animator.addListener(new AnimatorListenerAdapter() {
             private boolean mCancelled;
 
@@ -625,38 +627,54 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (clearAllExpandHack && !mCancelled) {
-                    setExpandedHeightInternal(getMaxPanelHeight());
-                }
-                setAnimator(null);
-                if (!mCancelled) {
-                    InteractionJankMonitor.getInstance()
-                            .end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
-                    notifyExpandingFinished();
+                if (expand && mFlingVelocity > 0) {
+                    // After the shade is flinged open to an overscrolled state, spring back
+                    // the shade by reducing section padding to 0.
+                    springBack();
                 } else {
-                    InteractionJankMonitor.getInstance()
-                            .cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+                    onFlingEnd(mCancelled);
                 }
-                notifyBarPanelExpansionChanged();
             }
         });
         setAnimator(animator);
         animator.start();
     }
 
-    /**
-     * When expanding, should we expand to the top of clear all and expand immediately?
-     * This will make sure that the animation will stop smoothly at the end of the last notification
-     * before the clear all affordance.
-     *
-     * @param targetHeight the height that we would animate to, right above clear all
-     *
-     * @return true if we can expand to the top of clear all
-     */
-    protected boolean shouldExpandToTopOfClearAll(float targetHeight) {
-        return fullyExpandedClearAllVisible()
-                && mExpandedHeight < targetHeight
-                && !isClearAllVisible();
+    private void springBack() {
+        ValueAnimator animator = ValueAnimator.ofFloat(MAX_OVERSCROLL, 0);
+        animator.addUpdateListener(
+                animation -> {
+                    setSectionPadding((float) animation.getAnimatedValue());
+                    setExpandedHeightInternal(mFlingTarget);
+                });
+        animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
+        animator.setInterpolator(Interpolators.LINEAR);
+        animator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCancelled;
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                onFlingEnd(mCancelled);
+            }
+        });
+        setAnimator(animator);
+        animator.start();
+    }
+
+    private void onFlingEnd(boolean cancelled) {
+        setAnimator(null);
+        if (!cancelled) {
+            InteractionJankMonitor.getInstance()
+                    .end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+            notifyExpandingFinished();
+        } else {
+            InteractionJankMonitor.getInstance()
+                    .cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+        }
+        notifyBarPanelExpansionChanged();
     }
 
     protected abstract boolean shouldUseDismissingAnimation();
@@ -697,10 +715,28 @@
         setExpandedHeight(currentMaxPanelHeight);
     }
 
+    private float getStackHeightFraction(float height) {
+        final float gestureFraction = height / getMaxPanelHeight();
+        final float stackHeightFraction = Interpolators.ACCELERATE_DECELERATE
+                .getInterpolation(gestureFraction);
+        return stackHeightFraction;
+    }
+
+    // When the shade is flinged open, add space before sections for overscroll effect.
+    private void maybeOverScrollForShadeFlingOpen(float height) {
+        if (!mBar.isShadeOpening() || mFlingVelocity <= 0) {
+            return;
+        }
+        final float padding = MathUtils.lerp(
+                MIN_OVERSCROLL, MAX_OVERSCROLL, getStackHeightFraction(height));
+        setSectionPadding(padding);
+    }
+
     public void setExpandedHeightInternal(float h) {
         if (isNaN(h)) {
             Log.wtf(TAG, "ExpandedHeight set to NaN");
         }
+        maybeOverScrollForShadeFlingOpen(h);
         if (mExpandLatencyTracking && h != 0f) {
             DejankUtils.postAfterTraversal(
                     () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
@@ -742,6 +778,8 @@
 
     protected abstract void setIsShadeOpening(boolean isShadeOpening);
 
+    protected abstract void setSectionPadding(float padding);
+
     protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
 
     protected abstract void onHeightUpdated(float expandedHeight);
@@ -1075,11 +1113,6 @@
 
     protected abstract boolean isClearAllVisible();
 
-    /**
-     * @return the height of the clear all button, in pixels including padding
-     */
-    protected abstract int getClearAllHeightWithPadding();
-
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 5a7c5c9..f65f97a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -271,7 +271,7 @@
         float contentStart = getPaddingStart();
         int childCount = getChildCount();
         // Underflow === don't show content until that index
-        if (DEBUG) android.util.Log.d(TAG, "calculateIconTranslations: start=" + translationX
+        if (DEBUG) Log.d(TAG, "calculateIconTranslations: start=" + translationX
                 + " width=" + width + " underflow=" + mNeedsUnderflow);
 
         // Collect all of the states which want to be visible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index f45178c..90f5577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -43,6 +43,8 @@
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 
+import java.util.ArrayList;
+
 /**
  * Manages the user switcher on the Keyguard.
  */
@@ -53,7 +55,7 @@
 
     private final Container mUserSwitcherContainer;
     private final KeyguardStatusBarView mStatusBarView;
-    private final Adapter mAdapter;
+    private final KeyguardUserAdapter mAdapter;
     private final AppearAnimationUtils mAppearAnimationUtils;
     private final KeyguardUserSwitcherScrim mBackground;
 
@@ -76,7 +78,7 @@
             mStatusBarView = statusBarView;
             mStatusBarView.setKeyguardUserSwitcher(this);
             panelViewController.setKeyguardUserSwitcher(this);
-            mAdapter = new Adapter(context, userSwitcherController, this);
+            mAdapter = new KeyguardUserAdapter(context, userSwitcherController, this);
             mAdapter.registerDataSetObserver(mDataSetObserver);
             mUserSwitcherController = userSwitcherController;
             mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
@@ -259,14 +261,16 @@
         }
     }
 
-    public static class Adapter extends UserSwitcherController.BaseUserAdapter implements
-            View.OnClickListener {
+    static class KeyguardUserAdapter extends
+            UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
 
         private Context mContext;
         private KeyguardUserSwitcher mKeyguardUserSwitcher;
         private View mCurrentUserView;
+        // List of users where the first entry is always the current user
+        private ArrayList<UserSwitcherController.UserRecord> mUsersOrdered = new ArrayList<>();
 
-        public Adapter(Context context, UserSwitcherController controller,
+        KeyguardUserAdapter(Context context, UserSwitcherController controller,
                 KeyguardUserSwitcher kgu) {
             super(controller);
             mContext = context;
@@ -274,15 +278,53 @@
         }
 
         @Override
+        public void notifyDataSetChanged() {
+            refreshUserOrder();
+            super.notifyDataSetChanged();
+        }
+
+        void refreshUserOrder() {
+            ArrayList<UserSwitcherController.UserRecord> users = super.getUsers();
+            mUsersOrdered = new ArrayList<>(users.size());
+            for (int i = 0; i < users.size(); i++) {
+                UserSwitcherController.UserRecord record = users.get(i);
+                if (record.isCurrent) {
+                    mUsersOrdered.add(0, record);
+                } else {
+                    mUsersOrdered.add(record);
+                }
+            }
+        }
+
+        @Override
+        protected ArrayList<UserSwitcherController.UserRecord> getUsers() {
+            return mUsersOrdered;
+        }
+
+        @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             UserSwitcherController.UserRecord item = getItem(position);
-            if (!(convertView instanceof UserDetailItemView)
+            return createUserDetailItemView(convertView, parent, item);
+        }
+
+        KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
+            if (!(convertView instanceof KeyguardUserDetailItemView)
                     || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
                 convertView = LayoutInflater.from(mContext).inflate(
                         R.layout.keyguard_user_switcher_item, parent, false);
-                convertView.setOnClickListener(this);
             }
-            UserDetailItemView v = (UserDetailItemView) convertView;
+            return (KeyguardUserDetailItemView) convertView;
+        }
+
+        UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
+                UserSwitcherController.UserRecord item) {
+            KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
+            if (!item.isCurrent || item.isGuest) {
+                v.setOnClickListener(this);
+            } else {
+                v.setOnClickListener(null);
+                v.setClickable(false);
+            }
 
             String name = getName(mContext, item);
             if (item.picture == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 92d013e..f1ccde7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -294,7 +294,9 @@
             }
         };
 
-        mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback);
+        if (mWifiManager != null) {
+            mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback);
+        }
 
         ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
             private Network mLastNetwork;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 9a8e26d..0552396 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -605,19 +605,23 @@
             controller.addAdapter(new WeakReference<>(this));
         }
 
+        protected ArrayList<UserRecord> getUsers() {
+            return mController.getUsers();
+        }
+
         public int getUserCount() {
             boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
                     && mKeyguardStateController.isMethodSecure()
                     && !mKeyguardStateController.canDismissLockScreen();
             if (!secureKeyguardShowing) {
-                return mController.getUsers().size();
+                return getUsers().size();
             }
             // The lock screen is secure and showing. Filter out restricted records.
-            final int N = mController.getUsers().size();
+            final int userSize = getUsers().size();
             int count = 0;
-            for (int i = 0; i < N; i++) {
-                if (mController.getUsers().get(i).isGuest) continue;
-                if (mController.getUsers().get(i).isRestricted) {
+            for (int i = 0; i < userSize; i++) {
+                if (getUsers().get(i).isGuest) continue;
+                if (getUsers().get(i).isRestricted) {
                     break;
                 } else {
                     count++;
@@ -632,13 +636,13 @@
                     && mKeyguardStateController.isMethodSecure()
                     && !mKeyguardStateController.canDismissLockScreen();
             if (!secureKeyguardShowing) {
-                return mController.getUsers().size();
+                return getUsers().size();
             }
             // The lock screen is secure and showing. Filter out restricted records.
-            final int N = mController.getUsers().size();
+            final int userSize = getUsers().size();
             int count = 0;
-            for (int i = 0; i < N; i++) {
-                if (mController.getUsers().get(i).isRestricted) {
+            for (int i = 0; i < userSize; i++) {
+                if (getUsers().get(i).isRestricted) {
                     break;
                 } else {
                     count++;
@@ -649,7 +653,7 @@
 
         @Override
         public UserRecord getItem(int position) {
-            return mController.getUsers().get(position);
+            return getUsers().get(position);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java
new file mode 100644
index 0000000..3a77f7ee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.media.systemsounds;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.media.AudioManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HomeSoundEffectControllerTest extends SysuiTestCase {
+
+    private @Mock Context mContext;
+    private @Mock AudioManager mAudioManager;
+    private @Mock TaskStackChangeListeners mTaskStackChangeListeners;
+    private @Mock ActivityManager.RunningTaskInfo mStandardActivityTaskInfo;
+    private @Mock ActivityManager.RunningTaskInfo mHomeActivityTaskInfo;
+
+    private HomeSoundEffectController mController;
+    private TaskStackChangeListener mTaskStackChangeListener;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(WindowConfiguration.ACTIVITY_TYPE_STANDARD).when(
+                mStandardActivityTaskInfo).getActivityType();
+        doReturn(WindowConfiguration.ACTIVITY_TYPE_HOME).when(
+                mHomeActivityTaskInfo).getActivityType();
+
+        mController = new HomeSoundEffectController(mContext, mAudioManager,
+                mTaskStackChangeListeners);
+    }
+
+    @Test
+    public void testHomeSoundEffectNotPlayedWhenHomeFirstMovesToFront() {
+        // When HomeSoundEffectController is started and the home sound effect is enabled,
+        startController(true /* isHomeSoundEffectEnabled */);
+
+        // And the home task moves to the front,
+        mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo);
+
+        // Then no home sound effect should be played.
+        verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME);
+    }
+
+    @Test
+    public void testHomeSoundEffectPlayedWhenEnabled() {
+        // When HomeSoundEffectController is started and the home sound effect is enabled,
+        startController(true /* isHomeSoundEffectEnabled */);
+
+        // And first a task different from the home task moves to front,
+        mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo);
+
+        // And the home task moves to the front,
+        mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo);
+
+        // Then the home sound effect should be played.
+        verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME);
+    }
+
+    @Test
+    public void testHomeSoundEffectNotPlayedTwiceInRow() {
+        // When HomeSoundEffectController is started and the home sound effect is enabled,
+        startController(true /* isHomeSoundEffectEnabled */);
+
+        // And first a task different from the home task moves to front,
+        mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo);
+
+        // And the home task moves to the front,
+        mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo);
+
+        // Then the home sound effect should be played.
+        verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME);
+
+        // If the home task moves to front a second time in a row,
+        mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo);
+
+        // Then no home sound effect should be played.
+        verify(mAudioManager, times(1)).playSoundEffect(AudioManager.FX_HOME);
+    }
+
+    @Test
+    public void testHomeSoundEffectNotPlayedWhenNonHomeTaskMovesToFront() {
+        // When HomeSoundEffectController is started and the home sound effect is enabled,
+        startController(true /* isHomeSoundEffectEnabled */);
+
+        // And a standard, non-home task, moves to the front,
+        mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo);
+
+        // Then no home sound effect should be played.
+        verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME);
+    }
+
+    @Test
+    public void testHomeSoundEffectDisabled() {
+        // When HomeSoundEffectController is started and the home sound effect is disabled,
+        startController(false /* isHomeSoundEffectEnabled */);
+
+        // Then no TaskStackListener should be registered
+        verify(mTaskStackChangeListeners, never()).registerTaskStackListener(
+                any(TaskStackChangeListener.class));
+    }
+
+    /**
+     * Sets {@link AudioManager#isHomeSoundEffectEnabled()} and starts HomeSoundEffectController.
+     * If the home sound effect is enabled, the registered TaskStackChangeListener is extracted.
+     */
+    private void startController(boolean isHomeSoundEffectEnabled) {
+        // Configure home sound effect to be enabled
+        doReturn(isHomeSoundEffectEnabled).when(mAudioManager).isHomeSoundEffectEnabled();
+
+        mController.start();
+
+        if (isHomeSoundEffectEnabled) {
+            // Construct controller. Save the TaskStackListener for injecting events.
+            final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
+                    ArgumentCaptor.forClass(TaskStackChangeListener.class);
+            verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture());
+            mTaskStackChangeListener = listenerCaptor.getValue();
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index c7c1823..ee1d758 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -52,6 +52,7 @@
     private boolean mHasCustomClock;
     private boolean mHasVisibleNotifs;
     private float mQsExpansion;
+    private int mCutoutTopInset = 0; // in pixels
 
     @Before
     public void setUp() {
@@ -394,7 +395,8 @@
         mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
                 mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY,
                 mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
-                0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */, mQsExpansion);
+                0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */,
+                mQsExpansion, mCutoutTopInset);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
new file mode 100644
index 0000000..fc1a791
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.statusbar.policy
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.testing.AndroidTestingRunner
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.internal.util.UserIcons
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.UserDetailItemView
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
+    @Mock
+    private lateinit var userSwitcherController: UserSwitcherController
+    @Mock
+    private lateinit var parent: ViewGroup
+    @Mock
+    private lateinit var keyguardUserDetailItemView: KeyguardUserDetailItemView
+    @Mock
+    private lateinit var otherView: View
+    @Mock
+    private lateinit var inflatedUserDetailItemView: KeyguardUserDetailItemView
+    @Mock
+    private lateinit var userInfo: UserInfo
+    @Mock
+    private lateinit var layoutInflater: LayoutInflater
+    @Mock
+    private lateinit var keyguardUserSwitcher: KeyguardUserSwitcher
+
+    private lateinit var adapter: KeyguardUserSwitcher.KeyguardUserAdapter
+    private lateinit var picture: Bitmap
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, layoutInflater)
+        `when`(layoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
+                .thenReturn(inflatedUserDetailItemView)
+        adapter = KeyguardUserSwitcher.KeyguardUserAdapter(mContext, userSwitcherController,
+                keyguardUserSwitcher)
+        picture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
+    }
+
+    /**
+     * Uses the KeyguardUserAdapter to create a UserDetailItemView where the convertView has an
+     * incompatible type
+     */
+    private fun createViewFromDifferentType(
+        isCurrentUser: Boolean,
+        isGuestUser: Boolean
+    ): UserDetailItemView? {
+        val user = createUserRecord(isCurrentUser, isGuestUser)
+        return adapter.createUserDetailItemView(otherView, parent, user)
+    }
+
+    /**
+     * Uses the KeyguardUserAdapter to create a UserDetailItemView where the convertView is an
+     * instance of KeyguardUserDetailItemView
+     */
+    private fun createViewFromSameType(
+        isCurrentUser: Boolean,
+        isGuestUser: Boolean
+    ): UserDetailItemView? {
+        val user = createUserRecord(isCurrentUser, isGuestUser)
+        return adapter.createUserDetailItemView(keyguardUserDetailItemView, parent, user)
+    }
+
+    @Test
+    fun shouldSetOnClickListener_notCurrentUser_notGuestUser_oldViewIsSameType() {
+        val v: UserDetailItemView? = createViewFromSameType(
+                isCurrentUser = false, isGuestUser = false)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(adapter)
+    }
+
+    @Test
+    fun shouldSetOnClickListener_notCurrentUser_guestUser_oldViewIsSameType() {
+        val v: UserDetailItemView? = createViewFromSameType(
+                isCurrentUser = false, isGuestUser = true)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(adapter)
+    }
+
+    @Test
+    fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
+        val v: UserDetailItemView? = createViewFromSameType(
+                isCurrentUser = true, isGuestUser = false)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(null)
+    }
+
+    @Test
+    fun shouldSetOnClickListener_currentUser_guestUser_oldViewIsSameType() {
+        val v: UserDetailItemView? = createViewFromSameType(
+                isCurrentUser = true, isGuestUser = true)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(adapter)
+    }
+
+    @Test
+    fun shouldSetOnClickListener_notCurrentUser_notGuestUser_oldViewIsDifferentType() {
+        val v: UserDetailItemView? = createViewFromDifferentType(
+                isCurrentUser = false, isGuestUser = false)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(adapter)
+    }
+
+    @Test
+    fun shouldSetOnClickListener_notCurrentUser_guestUser_oldViewIsDifferentType() {
+        val v: UserDetailItemView? = createViewFromDifferentType(
+                isCurrentUser = false, isGuestUser = true)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(adapter)
+    }
+
+    @Test
+    fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
+        val v: UserDetailItemView? = createViewFromDifferentType(
+                isCurrentUser = true, isGuestUser = false)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(null)
+    }
+
+    @Test
+    fun shouldSetOnClickListener_currentUser_guestUser_oldViewIsDifferentType() {
+        val v: UserDetailItemView? = createViewFromDifferentType(
+                isCurrentUser = true, isGuestUser = true)
+        assertNotNull(v)
+        verify(v)!!.setOnClickListener(adapter)
+    }
+
+    @Test
+    fun testCurrentUserIsAlwaysFirst() {
+        `when`(userSwitcherController.users).thenReturn(arrayListOf(
+                createUserRecord(isCurrentUser = false, isGuestUser = false),
+                createUserRecord(isCurrentUser = true, isGuestUser = false),
+                createUserRecord(isCurrentUser = false, isGuestUser = true),
+                createUserRecord(isCurrentUser = false, isGuestUser = false)
+        ))
+
+        adapter.notifyDataSetChanged()
+        assertTrue("Expected current user to be first in list", adapter.getItem(0).isCurrent)
+        assertFalse("Did not expect current user in position 1", adapter.getItem(1).isCurrent)
+        assertFalse("Did not expect current user in position 2", adapter.getItem(2).isCurrent)
+        assertTrue("Expected guest user to remain in position 2", adapter.getItem(2).isGuest)
+        assertFalse("Did not expect current user in position 3", adapter.getItem(3).isCurrent)
+    }
+
+    private fun createUserRecord(isCurrentUser: Boolean, isGuestUser: Boolean) =
+            UserSwitcherController.UserRecord(
+                    userInfo,
+                    picture,
+                    isGuestUser,
+                    isCurrentUser,
+                    false /* isAddUser */,
+                    false /* isRestricted */,
+                    true /* isSwitchToEnabled */)
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index c34285e..bab6deb 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -394,7 +394,7 @@
     static boolean isEmergencyGestureSettingEnabled(Context context, int userId) {
         return isEmergencyGestureEnabled(context.getResources())
                 && Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.EMERGENCY_GESTURE_ENABLED, 0, userId) != 0;
+                Settings.Secure.EMERGENCY_GESTURE_ENABLED, 1, userId) != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index b14ce1c..a6cfae4 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -8,7 +8,7 @@
 per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
 
 # Userspace reboot
-per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com
+per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com
 
 # Sensor Privacy
 per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index e99bb24..0aee780 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -16,10 +16,14 @@
 
 package com.android.server;
 
+import static android.app.ActivityManager.RunningServiceInfo;
+import static android.app.ActivityManager.RunningTaskInfo;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
@@ -28,15 +32,20 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Icon;
 import android.hardware.ISensorPrivacyListener;
 import android.hardware.ISensorPrivacyManager;
@@ -55,10 +64,12 @@
 import android.service.SensorPrivacyIndividualEnabledSensorProto;
 import android.service.SensorPrivacyServiceDumpProto;
 import android.service.SensorPrivacyUserProto;
+import android.text.Html;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.TypedXmlPullParser;
@@ -66,6 +77,7 @@
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FunctionalUtils;
@@ -83,6 +95,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 
@@ -91,6 +105,8 @@
 
     private static final String TAG = "SensorPrivacyService";
 
+    private static final int SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000;
+
     /** Version number indicating compatibility parsing the persisted file */
     private static final int CURRENT_PERSISTENCE_VERSION = 1;
     /** Version number indicating the persisted data needs upgraded to match new internal data
@@ -110,8 +126,6 @@
     private static final String SENSOR_PRIVACY_CHANNEL_ID = Context.SENSOR_PRIVACY_SERVICE;
     private static final String ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY =
             SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy";
-    private static final String EXTRA_SENSOR = SensorPrivacyService.class.getName()
-            + ".extra.sensor";
 
     // These are associated with fields that existed for older persisted versions of files
     private static final int VER0_ENABLED = 0;
@@ -121,11 +135,15 @@
 
     private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
     private final UserManagerInternal mUserManagerInternal;
+    private final ActivityManager mActivityManager;
+    private final ActivityTaskManager mActivityTaskManager;
 
     public SensorPrivacyService(Context context) {
         super(context);
         mUserManagerInternal = getLocalService(UserManagerInternal.class);
         mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(context);
+        mActivityManager = context.getSystemService(ActivityManager.class);
+        mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
     }
 
     @Override
@@ -134,7 +152,8 @@
     }
 
     class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements
-            AppOpsManager.OnOpNotedListener, AppOpsManager.OnOpStartedListener {
+            AppOpsManager.OnOpNotedListener, AppOpsManager.OnOpStartedListener,
+            IBinder.DeathRecipient {
 
         private final SensorPrivacyHandler mHandler;
         private final Context mContext;
@@ -146,6 +165,15 @@
         @GuardedBy("mLock")
         private SparseArray<SparseBooleanArray> mIndividualEnabled = new SparseArray<>();
 
+        /**
+         * Packages for which not to show sensor use reminders.
+         *
+         * <Package, User> -> list of suppressor tokens
+         */
+        @GuardedBy("mLock")
+        private ArrayMap<Pair<String, UserHandle>, ArrayList<IBinder>> mSuppressReminders =
+                new ArrayMap<>();
+
         SensorPrivacyServiceImpl(Context context) {
             mContext = context;
             mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext);
@@ -166,7 +194,9 @@
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    setIndividualSensorPrivacy(intent.getIntExtra(Intent.EXTRA_USER_ID, -1),
+                    setIndividualSensorPrivacy(
+                            ((UserHandle) intent.getParcelableExtra(
+                                    Intent.EXTRA_USER)).getIdentifier(),
                             intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
                 }
             }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY));
@@ -192,7 +222,12 @@
                 sensor = CAMERA;
             }
 
-            onSensorUseStarted(uid, packageName, sensor);
+            long token = Binder.clearCallingIdentity();
+            try {
+                onSensorUseStarted(uid, packageName, sensor);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         /**
@@ -203,31 +238,138 @@
          * @param sensor The sensor that is attempting to be used
          */
         private void onSensorUseStarted(int uid, String packageName, int sensor) {
-            int userId = UserHandle.getUserId(uid);
-            if (!isIndividualSensorPrivacyEnabled(userId, sensor)) {
+            UserHandle user = UserHandle.getUserHandleForUid(uid);
+            if (!isIndividualSensorPrivacyEnabled(user.getIdentifier(), sensor)) {
                 return;
             }
 
-            // TODO moltmann: Use dialog instead of notification if we can determine the activity
-            //                which triggered this usage
+            synchronized (mLock) {
+                if (mSuppressReminders.containsKey(new Pair<>(packageName, user))) {
+                    Log.d(TAG,
+                            "Suppressed sensor privacy reminder for " + packageName + "/" + user);
+                    return;
+                }
+            }
 
-            // TODO evanseverson: - Implement final UX for notification
-            //                    - Finalize strings and icons and add as resources
+            // TODO: Handle reminders with multiple sensors
 
-            int icon;
-            CharSequence notificationMessage;
+            // - If we have a likely activity that triggered the sensor use overlay a dialog over
+            //   it. This should be the most common case.
+            // - If there is no use visible entity that triggered the sensor don't show anything as
+            //   this is - from the point of the user - a background usage
+            // - Otherwise show a notification as we are not quite sure where to display the dialog.
+
+            List<RunningTaskInfo> tasksOfPackageUsingSensor = new ArrayList<>();
+
+            List<RunningTaskInfo> tasks = mActivityTaskManager.getTasks(Integer.MAX_VALUE);
+            int numTasks = tasks.size();
+            for (int taskNum = 0; taskNum < numTasks; taskNum++) {
+                RunningTaskInfo task = tasks.get(taskNum);
+
+                if (task.isVisible && task.topActivity.getPackageName().equals(packageName)) {
+                    if (task.isFocused) {
+                        // There is the one focused activity
+                        showSensorUseReminderDialog(task.taskId, user, packageName, sensor);
+                        return;
+                    }
+
+                    tasksOfPackageUsingSensor.add(task);
+                }
+            }
+
+            // TODO: Test this case
+            // There is one or more non-focused activity
+            if (tasksOfPackageUsingSensor.size() == 1) {
+                showSensorUseReminderDialog(tasksOfPackageUsingSensor.get(0).taskId, user,
+                        packageName, sensor);
+                return;
+            } else if (tasksOfPackageUsingSensor.size() > 1) {
+                showSensorUseReminderNotification(user, packageName, sensor);
+                return;
+            }
+
+            // TODO: Test this case
+            // Check if there is a foreground service for this package
+            List<RunningServiceInfo> services = mActivityManager.getRunningServices(
+                    Integer.MAX_VALUE);
+            int numServices = services.size();
+            for (int serviceNum = 0; serviceNum < numServices; serviceNum++) {
+                RunningServiceInfo service = services.get(serviceNum);
+
+                if (service.foreground && service.service.getPackageName().equals(packageName)) {
+                    showSensorUseReminderNotification(user, packageName, sensor);
+                    return;
+                }
+            }
+
+            Log.i(TAG, packageName + "/" + uid + " started using sensor " + sensor
+                    + " but no activity or foreground service was running. The user will not be"
+                    + " informed. System components should check if sensor privacy is enabled for"
+                    + " the sensor before accessing it.");
+        }
+
+        /**
+         * Show a dialog that informs the user that a sensor use or a blocked sensor started.
+         * The user can then react to this event.
+         *
+         * @param taskId The task this dialog should be overlaid on.
+         * @param user The user of the package using the sensor.
+         * @param packageName The name of the package using the sensor.
+         * @param sensor The sensor that is being used.
+         */
+        private void showSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
+                @NonNull String packageName, int sensor) {
+            Intent dialogIntent = new Intent();
+            dialogIntent.setComponent(ComponentName.unflattenFromString(
+                    mContext.getResources().getString(
+                            R.string.config_sensorUseStartedActivity)));
+
+            ActivityOptions options = ActivityOptions.makeBasic();
+            options.setLaunchTaskId(taskId);
+            options.setTaskOverlay(true, true);
+
+            dialogIntent.putExtra(EXTRA_PACKAGE_NAME, packageName);
+            dialogIntent.putExtra(EXTRA_SENSOR, sensor);
+
+            mContext.startActivityAsUser(dialogIntent, options.toBundle(), user);
+        }
+
+        /**
+         * Show a notification that informs the user that a sensor use or a blocked sensor started.
+         * The user can then react to this event.
+         *
+         * @param user The user of the package using the sensor.
+         * @param packageName The name of the package using the sensor.
+         * @param sensor The sensor that is being used.
+         */
+        private void showSensorUseReminderNotification(@NonNull UserHandle user,
+                @NonNull String packageName, int sensor) {
+            int iconRes;
+            int messageRes;
+
+            CharSequence packageLabel;
+            try {
+                packageLabel = getUiContext().getPackageManager()
+                        .getApplicationInfoAsUser(packageName, 0, user)
+                        .loadLabel(mContext.getPackageManager());
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Cannot show sensor use notification for " + packageName);
+                return;
+            }
+
             if (sensor == MICROPHONE) {
-                icon = com.android.internal.R.drawable.ic_mic;
-                notificationMessage = "Microphone is muted because of sensor privacy";
+                iconRes = R.drawable.ic_mic_blocked;
+                messageRes = R.string.sensor_privacy_start_use_mic_notification_content;
             } else {
-                icon = com.android.internal.R.drawable.ic_camera;
-                notificationMessage = "Camera is blocked because of sensor privacy";
+                iconRes = R.drawable.ic_camera_blocked;
+                messageRes = R.string.sensor_privacy_start_use_camera_notification_content;
             }
 
             NotificationManager notificationManager =
                     mContext.getSystemService(NotificationManager.class);
             NotificationChannel channel = new NotificationChannel(
-                    SENSOR_PRIVACY_CHANNEL_ID, "Sensor privacy",
+                    SENSOR_PRIVACY_CHANNEL_ID,
+                    getUiContext().getString(R.string.sensor_privacy_notification_channel_label),
                     NotificationManager.IMPORTANCE_HIGH);
             channel.setSound(null, null);
             channel.setBypassDnd(true);
@@ -236,18 +378,21 @@
 
             notificationManager.createNotificationChannel(channel);
 
+            Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
             notificationManager.notify(sensor,
                     new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
-                            .setContentTitle(notificationMessage)
+                            .setContentTitle(Html.fromHtml(getUiContext().getString(messageRes,
+                                    packageLabel),0))
                             .setSmallIcon(icon)
-                            .addAction(new Notification.Action.Builder(
-                                    Icon.createWithResource(mContext, icon),
-                                    "Disable sensor privacy",
+                            .setLargeIcon(icon)
+                            .addAction(new Notification.Action.Builder(icon,
+                                    getUiContext().getString(
+                                            R.string.sensor_privacy_start_use_dialog_turn_on_button),
                                     PendingIntent.getBroadcast(mContext, sensor,
                                             new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
                                                     .setPackage(mContext.getPackageName())
                                                     .putExtra(EXTRA_SENSOR, sensor)
-                                                    .putExtra(Intent.EXTRA_USER_ID, userId),
+                                                    .putExtra(Intent.EXTRA_USER, user),
                                             PendingIntent.FLAG_IMMUTABLE
                                                     | PendingIntent.FLAG_UPDATE_CURRENT))
                                     .build())
@@ -587,6 +732,88 @@
         }
 
         @Override
+        public void suppressIndividualSensorPrivacyReminders(int userId, String packageName,
+                IBinder token, boolean suppress) {
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(token);
+
+            Pair<String, UserHandle> key = new Pair<>(packageName, UserHandle.of(userId));
+
+            synchronized (mLock) {
+                if (suppress) {
+                    try {
+                        token.linkToDeath(this, 0);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Could not suppress sensor use reminder", e);
+                        return;
+                    }
+
+                    ArrayList<IBinder> suppressPackageReminderTokens = mSuppressReminders.get(key);
+                    if (suppressPackageReminderTokens == null) {
+                        suppressPackageReminderTokens = new ArrayList<>(1);
+                        mSuppressReminders.put(key, suppressPackageReminderTokens);
+                    }
+
+                    suppressPackageReminderTokens.add(token);
+                } else {
+                    mHandler.postDelayed(PooledLambda.obtainRunnable(
+                            SensorPrivacyServiceImpl::removeSuppressPackageReminderToken,
+                            this, key, token),
+                            SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS);
+                }
+            }
+        }
+
+        /**
+         * Remove a sensor use reminder suppression token.
+         *
+         * @param key Key the token is in
+         * @param token The token to remove
+         */
+        private void removeSuppressPackageReminderToken(@NonNull Pair<String, UserHandle> key,
+                @NonNull IBinder token) {
+            synchronized (mLock) {
+                ArrayList<IBinder> suppressPackageReminderTokens =
+                        mSuppressReminders.get(key);
+                if (suppressPackageReminderTokens == null) {
+                    Log.e(TAG, "No tokens for " + key);
+                    return;
+                }
+
+                boolean wasRemoved = suppressPackageReminderTokens.remove(token);
+                if (wasRemoved) {
+                    token.unlinkToDeath(this, 0);
+
+                    if (suppressPackageReminderTokens.isEmpty()) {
+                        mSuppressReminders.remove(key);
+                    }
+                } else {
+                    Log.w(TAG, "Could not remove sensor use reminder suppression token " + token
+                            + " from " + key);
+                }
+            }
+        }
+
+        /**
+         * A owner of a suppressor token died. Clean up.
+         *
+         * @param token The token that is invalid now.
+         */
+        @Override
+        public void binderDied(@NonNull IBinder token) {
+            synchronized (mLock) {
+                for (Pair<String, UserHandle> key : mSuppressReminders.keySet()) {
+                    removeSuppressPackageReminderToken(key, token);
+                }
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            // Handled in binderDied(IBinder)
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             Objects.requireNonNull(fd);
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index dde182b..464b6ed 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -52,7 +52,6 @@
 import android.telephony.CallQuality;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
-import android.telephony.CellLocation;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.CellSignalStrengthGsm;
@@ -585,8 +584,6 @@
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public TelephonyRegistry(Context context, ConfigurationProvider configurationProvider) {
-        CellLocation  location = CellLocation.getEmpty();
-
         mContext = context;
         mConfigurationProvider = configurationProvider;
         mBatteryStats = BatteryStatsService.getService();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5cc3274..fcb32a1 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -112,7 +112,6 @@
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
@@ -1047,9 +1046,11 @@
 
     @GuardedBy("mAm")
     void schedulePendingServiceStartLocked(String packageName, int userId) {
-        for (int i = mPendingBringups.size() - 1; i >= 0; i--) {
+        int totalPendings = mPendingBringups.size();
+        for (int i = totalPendings - 1; i >= 0 && totalPendings > 0;) {
             final ServiceRecord r = mPendingBringups.keyAt(i);
             if (r.userId != userId || !TextUtils.equals(r.packageName, packageName)) {
+                i--;
                 continue;
             }
             final ArrayList<Runnable> curPendingBringups = mPendingBringups.valueAt(i);
@@ -1057,8 +1058,22 @@
                 for (int j = curPendingBringups.size() - 1; j >= 0; j--) {
                     curPendingBringups.get(j).run();
                 }
+                curPendingBringups.clear();
             }
-            mPendingBringups.removeAt(i);
+            // Now, how many remaining ones we have after calling into above runnables
+            final int curTotalPendings = mPendingBringups.size();
+            // Don't call removeAt() here, as it could have been removed already by above runnables
+            mPendingBringups.remove(r);
+            if (totalPendings != curTotalPendings) {
+                // Okay, within the above Runnable.run(), the mPendingBringups is altered.
+                // Restart the loop, it won't call into those finished runnables
+                // since we've cleared the curPendingBringups above.
+                totalPendings = mPendingBringups.size();
+                i = totalPendings - 1;
+            } else {
+                totalPendings = mPendingBringups.size();
+                i--;
+            }
         }
     }
 
@@ -1069,10 +1084,13 @@
             stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
         }
         r.callStart = false;
-        FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid,
-                r.name.getPackageName(), r.name.getClassName(),
-                FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
-        mAm.mBatteryStatsService.noteServiceStartRunning(r.stats);
+
+        final int uid = r.appInfo.uid;
+        final String packageName = r.name.getPackageName();
+        final String serviceName = r.name.getClassName();
+        FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
+                serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
+        mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName);
         String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false, false);
         if (error != null) {
             return new ComponentName("!!", error);
@@ -1108,10 +1126,13 @@
             service.delayedStop = true;
             return;
         }
-        FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, service.appInfo.uid,
-                service.name.getPackageName(), service.name.getClassName(),
-                FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
-        mAm.mBatteryStatsService.noteServiceStopRunning(service.stats);
+
+        final int uid = service.appInfo.uid;
+        final String packageName = service.name.getPackageName();
+        final String serviceName = service.name.getClassName();
+        FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
+                serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
+        mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
         service.startRequested = false;
         if (service.tracker != null) {
             service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -1267,10 +1288,12 @@
                 }
             }
 
-            FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid,
-                    r.name.getPackageName(), r.name.getClassName(),
-                    FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
-            mAm.mBatteryStatsService.noteServiceStopRunning(r.stats);
+            final int uid = r.appInfo.uid;
+            final String packageName = r.name.getPackageName();
+            final String serviceName = r.name.getClassName();
+            FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
+                    serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
+            mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
             r.startRequested = false;
             if (r.tracker != null) {
                 r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -2876,15 +2899,7 @@
                     final Intent.FilterComparison filter
                             = new Intent.FilterComparison(service.cloneFilter());
                     final ServiceRestarter res = new ServiceRestarter();
-                    final BatteryStatsImpl.Uid.Pkg.Serv ss;
-                    final BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
-                    synchronized (stats) {
-                        ss = stats.getServiceStatsLocked(
-                                sInfo.applicationInfo.uid, name.getPackageName(),
-                                name.getClassName(), SystemClock.elapsedRealtime(),
-                                SystemClock.uptimeMillis());
-                    }
-                    r = new ServiceRecord(mAm, ss, className, name, definingPackageName,
+                    r = new ServiceRecord(mAm, className, name, definingPackageName,
                             definingUid, filter, sInfo, callingFromFg, res);
                     r.mRecentCallingPackage = callingPackage;
                     res.setService(r);
@@ -3428,9 +3443,13 @@
                 EventLogTags.writeAmCreateService(
                         r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
             }
-            FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_LAUNCH_REPORTED, r.appInfo.uid,
-                    r.name.getPackageName(), r.name.getClassName());
-            mAm.mBatteryStatsService.noteServiceStartLaunch(r.stats);
+
+            final int uid = r.appInfo.uid;
+            final String packageName = r.name.getPackageName();
+            final String serviceName = r.name.getClassName();
+            FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_LAUNCH_REPORTED, uid, packageName,
+                    serviceName);
+            mAm.mBatteryStatsService.noteServiceStartLaunch(uid, packageName, serviceName);
             mAm.notifyPackageUse(r.serviceInfo.packageName,
                                  PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
@@ -3777,7 +3796,8 @@
         smap.mDelayedStartList.remove(r);
 
         if (r.app != null) {
-            mAm.mBatteryStatsService.noteServiceStopLaunch(r.stats);
+            mAm.mBatteryStatsService.noteServiceStopLaunch(r.appInfo.uid, r.name.getPackageName(),
+                    r.name.getClassName());
             r.app.stopService(r);
             r.app.updateBoundClientUids();
             if (r.whitelistManager) {
@@ -4323,7 +4343,8 @@
         // Clear app state from services.
         for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
             ServiceRecord sr = app.getRunningServiceAt(i);
-            mAm.mBatteryStatsService.noteServiceStopLaunch(sr.stats);
+            mAm.mBatteryStatsService.noteServiceStopLaunch(sr.appInfo.uid, sr.name.getPackageName(),
+                    sr.name.getClassName());
             if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
                 sr.app.stopService(sr);
                 sr.app.updateBoundClientUids();
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 94643f1..ba8f190 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -94,6 +94,7 @@
     static final String KEY_PROCESS_CRASH_COUNT_RESET_INTERVAL =
             "process_crash_count_reset_interval";
     static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit";
+    static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -131,6 +132,7 @@
     private static final int DEFAULT_MAX_PHANTOM_PROCESSES = 32;
     private static final int DEFAULT_PROCESS_CRASH_COUNT_RESET_INTERVAL = 12 * 60 * 60 * 1000;
     private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12;
+    private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 10 * 1000;
 
 
     // Flag stored in the DeviceConfig API.
@@ -379,6 +381,13 @@
     // the foreground state.
     volatile long mFgsNotificationDeferralInterval = 10_000;
 
+    /*
+     * At boot time, broadcast receiver ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED and
+     * ACTION_PRE_BOOT_COMPLETED are temp allowlisted to start FGS for a duration of time in
+     * milliseconds.
+     */
+    volatile long mBootTimeTempAllowlistDuration = DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -563,6 +572,9 @@
                             case KEY_MAX_PHANTOM_PROCESSES:
                                 updateMaxPhantomProcesses();
                                 break;
+                            case KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION:
+                                updateBootTimeTempAllowListDuration();
+                                break;
                             default:
                                 break;
                         }
@@ -832,6 +844,13 @@
                 DEFAULT_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS);
     }
 
+    private void updateBootTimeTempAllowListDuration() {
+        mBootTimeTempAllowlistDuration = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION,
+                DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION);
+    }
+
     private void updateImperceptibleKillExemptions() {
         IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear();
         IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
@@ -1030,6 +1049,8 @@
         pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD);
         pw.print("  "); pw.print(KEY_MAX_PHANTOM_PROCESSES); pw.print("=");
         pw.println(MAX_PHANTOM_PROCESSES);
+        pw.print("  "); pw.print(KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION); pw.print("=");
+        pw.println(mBootTimeTempAllowlistDuration);
 
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1f48aeb..aada21d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6003,23 +6003,21 @@
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             String abiOverride, int zygotePolicyFlags) {
         return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
-                false /* mountExtStorageFull */, abiOverride, zygotePolicyFlags);
+                abiOverride, zygotePolicyFlags);
     }
 
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
-            boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride,
-            int zygotePolicyFlags) {
+            boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
         return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
-                false /* disableTestApiChecks */, mountExtStorageFull, abiOverride,
-                zygotePolicyFlags);
+                false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags);
     }
 
     // TODO: Move to ProcessList?
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             boolean disableHiddenApiChecks, boolean disableTestApiChecks,
-            boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {
+            String abiOverride, int zygotePolicyFlags) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -6054,8 +6052,7 @@
             mPersistentStartingProcesses.add(app);
             mProcessList.startProcessLocked(app, new HostingRecord("added application",
                     customProcess != null ? customProcess : app.processName),
-                    zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
-                    mountExtStorageFull, abiOverride);
+                    zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride);
         }
 
         return app;
@@ -7612,10 +7609,10 @@
                 crashInfo.throwLineNumber);
 
         FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED,
-                Binder.getCallingUid(),
+                (r != null) ? r.uid : -1,
                 eventType,
                 processName,
-                Binder.getCallingPid(),
+                (r != null) ? r.pid : -1,
                 (r != null && r.info != null) ? r.info.packageName : "",
                 (r != null && r.info != null) ? (r.info.isInstantApp()
                         ? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
@@ -14356,10 +14353,6 @@
                         "disable hidden API checks");
             }
 
-            // TODO(b/158750470): remove
-            final boolean mountExtStorageFull = isCallerShell()
-                    && (flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0;
-
             final long origId = Binder.clearCallingIdentity();
 
             ProcessRecord app;
@@ -14375,8 +14368,7 @@
                             UsageEvents.Event.SYSTEM_INTERACTION);
                 }
                 app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
-                        disableTestApiChecks, mountExtStorageFull, abiOverride,
-                        ZYGOTE_POLICY_FLAG_EMPTY);
+                        disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
             }
 
 
@@ -16810,6 +16802,13 @@
         public Intent getIntentForIntentSender(IIntentSender sender) {
             return ActivityManagerService.this.getIntentForIntentSender(sender);
         }
+
+        @Override
+        public long getBootTimeTempAllowListDuration() {
+            // Do not lock ActivityManagerService.this here, this API is called by
+            // PackageManagerService.
+            return mConstants.mBootTimeTempAllowlistDuration;
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 405ee72..4fb7abb 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -2498,44 +2498,56 @@
         }
     }
 
-    void noteServiceStartRunning(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+    void noteServiceStartRunning(int uid, String pkg, String name) {
         synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
+                    final BatteryStatsImpl.Uid.Pkg.Serv stats = mStats.getServiceStatsLocked(uid,
+                            pkg, name, elapsedRealtime, uptime);
                     stats.startRunningLocked(uptime);
                 }
             });
         }
     }
 
-    void noteServiceStopRunning(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+    void noteServiceStopRunning(int uid, String pkg, String name) {
         synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
+                    final BatteryStatsImpl.Uid.Pkg.Serv stats = mStats.getServiceStatsLocked(uid,
+                            pkg, name, elapsedRealtime, uptime);
                     stats.stopRunningLocked(uptime);
                 }
             });
         }
     }
 
-    void noteServiceStartLaunch(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+    void noteServiceStartLaunch(int uid, String pkg, String name) {
         synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
+                    final BatteryStatsImpl.Uid.Pkg.Serv stats = mStats.getServiceStatsLocked(uid,
+                            pkg, name, elapsedRealtime, uptime);
                     stats.startLaunchedLocked(uptime);
                 }
             });
         }
     }
 
-    void noteServiceStopLaunch(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+    void noteServiceStopLaunch(int uid, String pkg, String name) {
         synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
+                    final BatteryStatsImpl.Uid.Pkg.Serv stats = mStats.getServiceStatsLocked(uid,
+                            pkg, name, elapsedRealtime, uptime);
                     stats.stopLaunchedLocked(uptime);
                 }
             });
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index be63dd4..60b2467 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -18,7 +18,9 @@
 
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -39,6 +41,7 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ProgressReporter;
+import com.android.server.LocalServices;
 import com.android.server.UiThread;
 
 import java.util.List;
@@ -107,9 +110,20 @@
         EventLogTags.writeAmPreBoot(mUserId, componentName.getPackageName());
 
         mIntent.setComponent(componentName);
+        long duration = 10_000;
+        final ActivityManagerInternal amInternal =
+                LocalServices.getService(ActivityManagerInternal.class);
+        if (amInternal != null) {
+            duration = amInternal.getBootTimeTempAllowListDuration();
+        }
+        final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+        bOptions.setTemporaryAppWhitelistDuration(
+                BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                duration);
         synchronized (mService) {
             mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null,
-                    null, AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+                    null, AppOpsManager.OP_NONE, bOptions.toBundle(), true,
+                    false, ActivityManagerService.MY_PID,
                     Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
         }
     }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6f6cad0..47b7e1b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1737,7 +1737,7 @@
     @GuardedBy("mService")
     boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
             int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
-            boolean mountExtStorageFull, String abiOverride) {
+            String abiOverride) {
         if (app.pendingStart) {
             return true;
         }
@@ -2331,7 +2331,7 @@
             int zygotePolicyFlags, String abiOverride) {
         return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
                 false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
-                false /* mountExtStorageFull */, abiOverride);
+                abiOverride);
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index d9c83da..8a1b4e3 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -46,7 +46,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.ServiceState;
-import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.LocalServices;
 import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.uri.NeededUriGrants;
@@ -70,7 +69,6 @@
     static final int MAX_DONE_EXECUTING_COUNT = 6;
 
     final ActivityManagerService ams;
-    final BatteryStatsImpl.Uid.Pkg.Serv stats;
     final ComponentName name; // service component.
     final ComponentName instanceName; // service component's per-instance name.
     final String shortInstanceName; // instanceName.flattenToShortString().
@@ -518,13 +516,11 @@
         }
     }
 
-    ServiceRecord(ActivityManagerService ams,
-            BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
+    ServiceRecord(ActivityManagerService ams, ComponentName name,
             ComponentName instanceName, String definingPackageName, int definingUid,
             Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
             Runnable restarter) {
         this.ams = ams;
-        this.stats = servStats;
         this.name = name;
         this.instanceName = instanceName;
         shortInstanceName = instanceName.flattenToShortString();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d73de7c..ffe1d68 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -45,8 +45,10 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.Dialog;
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
@@ -518,7 +520,9 @@
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
                         new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                        AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+                        AppOpsManager.OP_NONE,
+                        getTemporaryAppWhitelistBroadcastOptions().toBundle(), true,
+                        false, MY_PID, SYSTEM_UID,
                         Binder.getCallingUid(), Binder.getCallingPid(), userId);
             }
         }
@@ -764,7 +768,9 @@
                         }
                     }, 0, null, null,
                     new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                    AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+                    AppOpsManager.OP_NONE,
+                    getTemporaryAppWhitelistBroadcastOptions().toBundle(), true,
+                    false, MY_PID, SYSTEM_UID,
                     callingUid, callingPid, userId);
         });
     }
@@ -2804,6 +2810,20 @@
         }
     }
 
+    private BroadcastOptions getTemporaryAppWhitelistBroadcastOptions() {
+        long duration = 10_000;
+        final ActivityManagerInternal amInternal =
+                LocalServices.getService(ActivityManagerInternal.class);
+        if (amInternal != null) {
+            duration = amInternal.getBootTimeTempAllowListDuration();
+        }
+        final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+        bOptions.setTemporaryAppWhitelistDuration(
+                BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                duration);
+        return bOptions;
+    }
+
     /**
      * Helper class to store user journey and session id.
      *
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 17627fa..7115c9a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7724,20 +7724,24 @@
 
     private class MyHdmiControlStatusChangeListenerCallback
             implements HdmiControlManager.HdmiControlStatusChangeListener {
-        public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
+        public void onStatusChange(@HdmiControlManager.HdmiCecControl int isCecEnabled,
+                boolean isCecAvailable) {
             synchronized (mHdmiClientLock) {
                 if (mHdmiManager == null) return;
-                updateHdmiCecSinkLocked(isCecEnabled ? isCecAvailable : false);
+                boolean cecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+                updateHdmiCecSinkLocked(cecEnabled ? isCecAvailable : false);
             }
         }
     };
 
     private class MyHdmiCecVolumeControlFeatureListener
             implements HdmiControlManager.HdmiCecVolumeControlFeatureListener {
-        public void onHdmiCecVolumeControlFeature(boolean enabled) {
+        public void onHdmiCecVolumeControlFeature(
+                @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
             synchronized (mHdmiClientLock) {
                 if (mHdmiManager == null) return;
-                mHdmiCecVolumeControlEnabled = enabled;
+                mHdmiCecVolumeControlEnabled =
+                        hdmiCecVolumeControl == HdmiControlManager.VOLUME_CONTROL_ENABLED;
             }
         }
     };
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 5025c4a..b1633b0 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -288,6 +288,7 @@
     @GuardedBy("mAudioFocusLock")
     private void removeFocusStackEntry(String clientToRemove, boolean signal,
             boolean notifyFocusFollowers) {
+        AudioFocusInfo abandonSource = null;
         // is the current top of the focus stack abandoning focus? (because of request, not death)
         if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
         {
@@ -295,9 +296,7 @@
             FocusRequester fr = mFocusStack.pop();
             fr.release();
             if (notifyFocusFollowers) {
-                final AudioFocusInfo afi = fr.toAudioFocusInfo();
-                afi.clearLossReceived();
-                notifyExtPolicyFocusLoss_syncAf(afi, false);
+                abandonSource = fr.toAudioFocusInfo();
             }
             if (signal) {
                 // notify the new top of the stack it gained focus
@@ -315,11 +314,19 @@
                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
                             + clientToRemove);
                     stackIterator.remove();
+                    if (notifyFocusFollowers) {
+                        abandonSource = fr.toAudioFocusInfo();
+                    }
                     // stack entry not used anymore, clear references
                     fr.release();
                 }
             }
         }
+        // focus followers still want to know focus was abandoned, handled as a loss
+        if (abandonSource != null) {
+            abandonSource.clearLossReceived();
+            notifyExtPolicyFocusLoss_syncAf(abandonSource, false);
+        }
 
         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
             Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 61e7c89..da76af8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -160,4 +160,18 @@
             mFaceServiceReceiver.onChallengeInterruptFinished(sensorId);
         }
     }
+
+    // Fingerprint-specific callbacks for FingerprintManager only
+
+    public void onUdfpsPointerDown(int sensorId, int cookie) throws RemoteException {
+        if (mFingerprintServiceReceiver != null) {
+            mFingerprintServiceReceiver.onUdfpsPointerDown(sensorId);
+        }
+    }
+
+    public void onUdfpsPointerUp(int sensorId, int cookie) throws RemoteException {
+        if (mFingerprintServiceReceiver != null) {
+            mFingerprintServiceReceiver.onUdfpsPointerUp(sensorId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index e95447b..c2a30be 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -96,6 +96,16 @@
         public void onChallengeGenerated(int sensorId, long challenge) {
 
         }
+
+        @Override
+        public void onUdfpsPointerDown(int sensorId) {
+
+        }
+
+        @Override
+        public void onUdfpsPointerUp(int sensorId) {
+
+        }
     };
 
     BiometricTestSessionImpl(@NonNull Context context, int sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index c413c8b..bd57fea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -117,6 +117,9 @@
     public void onPointerDown(int x, int y, float minor, float major) {
         try {
             getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
+            if (getListener() != null) {
+                getListener().onUdfpsPointerDown(getSensorId(), getCookie());
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         }
@@ -126,6 +129,9 @@
     public void onPointerUp() {
         try {
             getFreshDaemon().onPointerUp(0 /* pointerId */);
+            if (getListener() != null) {
+                getListener().onUdfpsPointerUp(getSensorId(), getCookie());
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 95c4cee..6893e72 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -97,6 +97,16 @@
         public void onChallengeGenerated(int sensorId, long challenge) {
 
         }
+
+        @Override
+        public void onUdfpsPointerDown(int sensorId) {
+
+        }
+
+        @Override
+        public void onUdfpsPointerUp(int sensorId) {
+
+        }
     };
 
     BiometricTestSessionImpl(@NonNull Context context, int sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 13e2e4f..589db6c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -150,11 +150,25 @@
     @Override
     public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
+        if (getListener() != null) {
+            try {
+                getListener().onUdfpsPointerDown(getSensorId(), getCookie());
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception", e);
+            }
+        }
     }
 
     @Override
     public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
+        if (getListener() != null) {
+            try {
+                getListener().onUdfpsPointerUp(getSensorId(), getCookie());
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception", e);
+            }
+        }
     }
 
     public boolean isKeyguard() {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 9376e8d..69686a2 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.os.Environment;
-import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.LongArray;
 import android.util.LongSparseArray;
@@ -53,7 +52,7 @@
 import javax.xml.datatype.DatatypeConfigurationException;
 
 /**
- * This class maintains state relating to platform compatibility changes.
+ * CompatConfig maintains state related to the platform compatibility changes.
  *
  * <p>It stores the default configuration for each change, and any per-package overrides that have
  * been configured.
@@ -65,18 +64,38 @@
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
-    private OverrideValidatorImpl mOverrideValidator;
+    private final OverrideValidatorImpl mOverrideValidator;
 
     @VisibleForTesting
     CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
         mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
     }
 
+    static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
+        CompatConfig config = new CompatConfig(androidBuildClassifier, context);
+        config.initConfigFromLib(Environment.buildPath(
+                Environment.getRootDirectory(), "etc", "compatconfig"));
+        config.initConfigFromLib(Environment.buildPath(
+                Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
+
+        List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
+        for (ApexManager.ActiveApexInfo apex : apexes) {
+            config.initConfigFromLib(Environment.buildPath(
+                    apex.apexDirectory, "etc", "compatconfig"));
+        }
+        config.invalidateCache();
+        return config;
+    }
+
     /**
-     * Add a change. This is intended to be used by code that reads change config from the
-     * filesystem. This should be done at system startup time.
+     * Adds a change.
      *
-     * @param change The change to add. Any change with the same ID will be overwritten.
+     * <p>This is intended to be used by code that reads change config from the filesystem. This
+     * should be done at system startup time.
+     *
+     * <p>Any change with the same ID will be overwritten.
+     *
+     * @param change the change to add
      */
     void addChange(CompatChange change) {
         synchronized (mChanges) {
@@ -86,13 +105,15 @@
     }
 
     /**
-     * Retrieves the set of disabled changes for a given app. Any change ID not in the returned
-     * array is by default enabled for the app.
+     * Retrieves the set of disabled changes for a given app.
      *
-     * @param app The app in question
-     * @return A sorted long array of change IDs. We use a primitive array to minimize memory
-     * footprint: Every app process will store this array statically so we aim to reduce
-     * overhead as much as possible.
+     * <p>Any change ID not in the returned array is by default enabled for the app.
+     *
+     * <p>We use a primitive array to minimize memory footprint: every app process will store this
+     * array statically so we aim to reduce overhead as much as possible.
+     *
+     * @param app the app in question
+     * @return a sorted long array of change IDs
      */
     long[] getDisabledChanges(ApplicationInfo app) {
         LongArray disabled = new LongArray();
@@ -110,10 +131,10 @@
     }
 
     /**
-     * Look up a change ID by name.
+     * Looks up a change ID by name.
      *
-     * @param name Name of the change to look up
-     * @return The change ID, or {@code -1} if no change with that name exists.
+     * @param name name of the change to look up
+     * @return the change ID, or {@code -1} if no change with that name exists
      */
     long lookupChangeId(String name) {
         synchronized (mChanges) {
@@ -127,10 +148,10 @@
     }
 
     /**
-     * Find if a given change is enabled for a given application.
+     * Checks if a given change is enabled for a given application.
      *
-     * @param changeId The ID of the change in question
-     * @param app      App to check for
+     * @param changeId the ID of the change in question
+     * @param app      app to check for
      * @return {@code true} if the change is enabled for this app. Also returns {@code true} if the
      * change ID is not known, as unknown changes are enabled by default.
      */
@@ -146,10 +167,10 @@
     }
 
     /**
-     * Find if a given change will be enabled for a given package name, prior to installation.
+     * Checks if a given change will be enabled for a given package name after the installation.
      *
-     * @param changeId    The ID of the change in question
-     * @param packageName Package name to check for
+     * @param changeId    the ID of the change in question
+     * @param packageName package name to check for
      * @return {@code true} if the change would be enabled for this package name. Also returns
      * {@code true} if the change ID is not known, as unknown changes are enabled by default.
      */
@@ -165,22 +186,22 @@
     }
 
     /**
-     * Overrides the enabled state for a given change and app. This method is intended to be used
-     * *only* for debugging purposes, ultimately invoked either by an adb command, or from some
-     * developer settings UI.
+     * Overrides the enabled state for a given change and app.
      *
-     * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
+     * <p>This method is intended to be used *only* for debugging purposes, ultimately invoked
+     * either by an adb command, or from some developer settings UI.
      *
-     * @param changeId    The ID of the change to be overridden. Note, this call will succeed even
-     *                    if
-     *                    this change is not known; it will only have any effect if any code in the
-     *                    platform is gated on the ID given.
-     * @param packageName The app package name to override the change for.
-     * @param enabled     If the change should be enabled or disabled.
-     * @return {@code true} if the change existed before adding the override.
+     * <p>Note: package overrides are not persistent and will be lost on system or runtime restart.
+     *
+     * @param changeId    the ID of the change to be overridden. Note, this call will succeed even
+     *                    if this change is not known; it will only have any effect if any code in
+     *                    the platform is gated on the ID given.
+     * @param packageName the app package name to override the change for
+     * @param enabled     if the change should be enabled or disabled
+     * @return {@code true} if the change existed before adding the override
+     * @throws IllegalStateException if overriding is not allowed
      */
-    boolean addOverride(long changeId, String packageName, boolean enabled)
-            throws SecurityException {
+    boolean addOverride(long changeId, String packageName, boolean enabled) {
         boolean alreadyKnown = true;
         OverrideAllowedState allowedState =
                 mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -201,18 +222,14 @@
                     break;
                 default:
                     throw new IllegalStateException("Should only be able to override changes that "
-                                                    + "are allowed or can be deferred.");
+                            + "are allowed or can be deferred.");
             }
             invalidateCache();
         }
         return alreadyKnown;
     }
 
-    /**
-     * Check whether the change is known to the compat config.
-     *
-     * @return {@code true} if the change is known.
-     */
+    /** Checks whether the change is known to the compat config. */
     boolean isKnownChangeId(long changeId) {
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
@@ -221,16 +238,13 @@
     }
 
     /**
-     * Returns the maximum sdk version for which this change can be opted in (or -1 if it is not
-     * target sdk gated).
+     * Returns the maximum SDK version for which this change can be opted in (or -1 if it is not
+     * target SDK gated).
      */
     int maxTargetSdkForChangeIdOptIn(long changeId) {
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
-            if (c == null) {
-                return -1;
-            }
-            if (c.getEnableSinceTargetSdk() != -1) {
+            if (c != null && c.getEnableSinceTargetSdk() != -1) {
                 return c.getEnableSinceTargetSdk() - 1;
             }
             return -1;
@@ -243,10 +257,7 @@
     boolean isLoggingOnly(long changeId) {
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
-            if (c == null) {
-                return false;
-            }
-            return c.getLoggingOnly();
+            return c != null && c.getLoggingOnly();
         }
     }
 
@@ -256,24 +267,21 @@
     boolean isDisabled(long changeId) {
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
-            if (c == null) {
-                return false;
-            }
-            return c.getDisabled();
+            return c != null && c.getDisabled();
         }
     }
 
     /**
-     * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This
-     * restores the default behaviour for the given change and app, once any app processes have been
-     * restarted.
+     * Removes an override previously added via {@link #addOverride(long, String, boolean)}.
      *
-     * @param changeId    The ID of the change that was overridden.
-     * @param packageName The app package name that was overridden.
+     * <p>This restores the default behaviour for the given change and app, once any app processes
+     * have been restarted.
+     *
+     * @param changeId    the ID of the change that was overridden
+     * @param packageName the app package name that was overridden
      * @return {@code true} if an override existed;
      */
-    boolean removeOverride(long changeId, String packageName)
-            throws SecurityException {
+    boolean removeOverride(long changeId, String packageName) {
         boolean overrideExists = false;
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
@@ -299,13 +307,12 @@
     /**
      * Overrides the enabled state for a given change and app.
      *
-     * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
+     * <p>Note: package overrides are not persistent and will be lost on system or runtime restart.
      *
-     * @param overrides   list of overrides to default changes config.
-     * @param packageName app for which the overrides will be applied.
+     * @param overrides   list of overrides to default changes config
+     * @param packageName app for which the overrides will be applied
      */
-    void addOverrides(CompatibilityChangeConfig overrides, String packageName)
-            throws RemoteException, SecurityException {
+    void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
         synchronized (mChanges) {
             for (Long changeId : overrides.enabledChanges()) {
                 addOverride(changeId, packageName, true);
@@ -324,9 +331,9 @@
      *
      * <p>This restores the default behaviour for the given app.
      *
-     * @param packageName The package for which the overrides should be purged.
+     * @param packageName the package for which the overrides should be purged
      */
-    void removePackageOverrides(String packageName) throws SecurityException {
+    void removePackageOverrides(String packageName) {
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange change = mChanges.valueAt(i);
@@ -337,8 +344,7 @@
     }
 
     private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
-                                                             int targetSdkVersion)
-                    throws RemoteException {
+            int targetSdkVersion) {
         LongArray allowed = new LongArray();
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
@@ -348,7 +354,7 @@
                 }
                 OverrideAllowedState allowedState =
                         mOverrideValidator.getOverrideAllowedState(change.getId(),
-                                                                    packageName);
+                                packageName);
                 if (allowedState.state == OverrideAllowedState.ALLOWED) {
                     allowed.add(change.getId());
                 }
@@ -361,10 +367,9 @@
      * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
      * {@param packageName}.
      *
-     * @return The number of changes that were toggled.
+     * @return the number of changes that were toggled
      */
-    int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
-            throws RemoteException {
+    int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
         long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
             addOverride(changeId, packageName, true);
@@ -372,15 +377,13 @@
         return changes.length;
     }
 
-
     /**
      * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
      * {@param packageName}.
      *
-     * @return The number of changes that were toggled.
+     * @return the number of changes that were toggled
      */
-    int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
-            throws RemoteException {
+    int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
         long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
             addOverride(changeId, packageName, false);
@@ -425,7 +428,7 @@
     /**
      * Dumps the current list of compatibility config information.
      *
-     * @param pw The {@link PrintWriter} instance to which the information will be dumped.
+     * @param pw {@link PrintWriter} instance to which the information will be dumped
      */
     void dumpConfig(PrintWriter pw) {
         synchronized (mChanges) {
@@ -441,13 +444,10 @@
     }
 
     /**
-     * Get the config for a given app.
+     * Returns config for a given app.
      *
-     * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped.
-     * @return A {@link CompatibilityChangeConfig} which contains the compat config info for the
-     * given app.
+     * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped
      */
-
     CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
         Set<Long> enabled = new HashSet<>();
         Set<Long> disabled = new HashSet<>();
@@ -467,7 +467,7 @@
     /**
      * Dumps all the compatibility change information.
      *
-     * @return An array of {@link CompatibilityChangeInfo} with the current changes.
+     * @return an array of {@link CompatibilityChangeInfo} with the current changes
      */
     CompatibilityChangeInfo[] dumpChanges() {
         synchronized (mChanges) {
@@ -480,22 +480,6 @@
         }
     }
 
-    static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
-        CompatConfig config = new CompatConfig(androidBuildClassifier, context);
-        config.initConfigFromLib(Environment.buildPath(
-                Environment.getRootDirectory(), "etc", "compatconfig"));
-        config.initConfigFromLib(Environment.buildPath(
-                Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
-
-        List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
-        for (ApexManager.ActiveApexInfo apex : apexes) {
-            config.initConfigFromLib(Environment.buildPath(
-                    apex.apexDirectory, "etc", "compatconfig"));
-        }
-        config.invalidateCache();
-        return config;
-    }
-
     void initConfigFromLib(File libraryDir) {
         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
             Slog.d(TAG, "No directory " + libraryDir + ", skipping");
@@ -526,6 +510,7 @@
     private void invalidateCache() {
         ChangeIdStateCache.invalidate();
     }
+
     /**
      * Rechecks all the existing overrides for a package.
      */
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 1ea468c..6b2a1c9 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -63,45 +63,43 @@
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
 
-    private static int sMinTargetSdk = Build.VERSION_CODES.Q;
-
     public PlatformCompat(Context context) {
         mContext = context;
-        mChangeReporter = new ChangeReporter(
-                ChangeReporter.SOURCE_SYSTEM_SERVER);
+        mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
     }
 
     @VisibleForTesting
     PlatformCompat(Context context, CompatConfig compatConfig) {
         mContext = context;
-        mChangeReporter = new ChangeReporter(
-                ChangeReporter.SOURCE_SYSTEM_SERVER);
+        mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = compatConfig;
+
         registerPackageReceiver(context);
     }
 
     @Override
     public void reportChange(long changeId, ApplicationInfo appInfo) {
-        checkCompatChangeLogPermission();
-        reportChange(changeId, appInfo.uid,
-                ChangeReporter.STATE_LOGGED);
+        reportChangeByUid(changeId, appInfo.uid);
     }
 
     @Override
-    public void reportChangeByPackageName(long changeId, String packageName, int userId) {
-        checkCompatChangeLogPermission();
+    public void reportChangeByPackageName(long changeId, String packageName,
+            @UserIdInt int userId) {
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
-        if (appInfo == null) {
-            return;
+        if (appInfo != null) {
+            reportChangeByUid(changeId, appInfo.uid);
         }
-        reportChange(changeId, appInfo);
     }
 
     @Override
     public void reportChangeByUid(long changeId, int uid) {
         checkCompatChangeLogPermission();
-        reportChange(changeId, uid, ChangeReporter.STATE_LOGGED);
+        reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED);
+    }
+
+    private void reportChangeInternal(long changeId, int uid, int state) {
+        mChangeReporter.reportChange(uid, changeId, state);
     }
 
     @Override
@@ -110,28 +108,6 @@
         return isChangeEnabledInternal(changeId, appInfo);
     }
 
-    /**
-     * Internal version of the above method, without logging. Does not perform costly permission
-     * check.
-     * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
-     */
-    public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
-        return mCompatConfig.isChangeEnabled(changeId, appInfo);
-    }
-
-    /**
-     * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly
-     * permission check.
-     */
-    public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
-        boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
-        if (appInfo != null) {
-            reportChange(changeId, appInfo.uid,
-                    enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
-        }
-        return enabled;
-    }
-
     @Override
     public boolean isChangeEnabledByPackageName(long changeId, String packageName,
             @UserIdInt int userId) {
@@ -140,7 +116,7 @@
         if (appInfo == null) {
             return mCompatConfig.willChangeBeEnabled(changeId, packageName);
         }
-        return isChangeEnabled(changeId, appInfo);
+        return isChangeEnabledInternal(changeId, appInfo);
     }
 
     @Override
@@ -152,81 +128,82 @@
         }
         boolean enabled = true;
         for (String packageName : packages) {
-            enabled = enabled && isChangeEnabledByPackageName(changeId, packageName,
+            enabled &= isChangeEnabledByPackageName(changeId, packageName,
                     UserHandle.getUserId(uid));
         }
         return enabled;
     }
 
     /**
-     * Register a listener for change state overrides. Only one listener per change is allowed.
+     * Internal version of the above method, without logging.
      *
-     * <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with
-     * packageName before the app is killed upon an override change. The state of a change is not
-     * guaranteed to change when {@code listener.onCompatChange(String)} is called.
-     *
-     * @param changeId to get updates for
-     * @param listener the listener that will be called upon a potential change for package.
-     * @throws IllegalStateException if a listener was already registered for changeId
-     * @returns {@code true} if a change with changeId was already known, or (@code false}
-     * otherwise.
+     * <p>Does not perform costly permission check.
+     * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
      */
-    public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
-        return mCompatConfig.registerListener(changeId, listener);
+    public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
+        return mCompatConfig.isChangeEnabled(changeId, appInfo);
+    }
+
+    /**
+     * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}.
+     *
+     * <p>Does not perform costly permission check.
+     */
+    public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
+        boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
+        if (appInfo != null) {
+            reportChangeInternal(changeId, appInfo.uid,
+                    enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
+        }
+        return enabled;
     }
 
     @Override
-    public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
-            throws RemoteException, SecurityException {
+    public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
         checkCompatChangeOverridePermission();
         mCompatConfig.addOverrides(overrides, packageName);
         killPackage(packageName);
     }
 
     @Override
-    public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
-            throws RemoteException, SecurityException {
+    public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
         checkCompatChangeOverridePermission();
         mCompatConfig.addOverrides(overrides, packageName);
     }
 
     @Override
-    public int enableTargetSdkChanges(String packageName, int targetSdkVersion)
-            throws RemoteException, SecurityException {
+    public int enableTargetSdkChanges(String packageName, int targetSdkVersion) {
         checkCompatChangeOverridePermission();
-        int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName,
-                                                                        targetSdkVersion);
+        int numChanges =
+                mCompatConfig.enableTargetSdkChangesForPackage(packageName, targetSdkVersion);
         killPackage(packageName);
         return numChanges;
     }
 
     @Override
-    public int disableTargetSdkChanges(String packageName, int targetSdkVersion)
-            throws RemoteException, SecurityException {
+    public int disableTargetSdkChanges(String packageName, int targetSdkVersion) {
         checkCompatChangeOverridePermission();
-        int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName,
-                                                                         targetSdkVersion);
+        int numChanges =
+                mCompatConfig.disableTargetSdkChangesForPackage(packageName, targetSdkVersion);
         killPackage(packageName);
         return numChanges;
     }
 
     @Override
-    public void clearOverrides(String packageName) throws RemoteException, SecurityException {
+    public void clearOverrides(String packageName) {
         checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
         killPackage(packageName);
     }
 
     @Override
-    public void clearOverridesForTest(String packageName)
-            throws RemoteException, SecurityException {
+    public void clearOverridesForTest(String packageName) {
         checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
     }
 
     @Override
-    public boolean clearOverride(long changeId, String packageName)
-            throws RemoteException, SecurityException {
+    public boolean clearOverride(long changeId, String packageName) {
         checkCompatChangeOverridePermission();
         boolean existed = mCompatConfig.removeOverride(changeId, packageName);
         killPackage(packageName);
@@ -234,6 +211,12 @@
     }
 
     @Override
+    public void clearOverrideForTest(long changeId, String packageName) {
+        checkCompatChangeOverridePermission();
+        mCompatConfig.removeOverride(changeId, packageName);
+    }
+
+    @Override
     public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
         checkCompatChangeReadAndLogPermission();
         return mCompatConfig.getAppConfig(appInfo);
@@ -247,18 +230,13 @@
 
     @Override
     public CompatibilityChangeInfo[] listUIChanges() {
-        return Arrays.stream(listAllChanges()).filter(
-                x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new);
+        return Arrays.stream(listAllChanges()).filter(this::isShownInUI).toArray(
+                CompatibilityChangeInfo[]::new);
     }
 
-    /**
-     * Check whether the change is known to the compat config.
-     *
-     * @return {@code true} if the change is known.
-     */
+    /** Checks whether the change is known to the compat config. */
     public boolean isKnownChangeId(long changeId) {
         return mCompatConfig.isKnownChangeId(changeId);
-
     }
 
     /**
@@ -286,7 +264,9 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) {
+            return;
+        }
         checkCompatChangeReadAndLogPermission();
         mCompatConfig.dumpConfig(pw);
     }
@@ -298,7 +278,8 @@
 
     /**
      * Clears information stored about events reported on behalf of an app.
-     * To be called once upon app start or end. A second call would be a no-op.
+     *
+     * <p>To be called once upon app start or end. A second call would be a no-op.
      *
      * @param appInfo the app to reset
      */
@@ -311,13 +292,9 @@
                 packageName, 0, userId, userId);
     }
 
-    private void reportChange(long changeId, int uid, int state) {
-        mChangeReporter.reportChange(uid, changeId, state);
-    }
-
     private void killPackage(String packageName) {
         int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName,
-                    0, UserHandle.myUserId());
+                0, UserHandle.myUserId());
 
         if (uid < 0) {
             Slog.w(TAG, "Didn't find package " + packageName + " on device.");
@@ -325,21 +302,18 @@
         }
 
         Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ").");
-        killUid(UserHandle.getAppId(uid),
-                UserHandle.USER_ALL, "PlatformCompat overrides");
+        killUid(UserHandle.getAppId(uid));
     }
 
-    private void killUid(int appId, int userId, String reason) {
+    private void killUid(int appId) {
         final long identity = Binder.clearCallingIdentity();
         try {
             IActivityManager am = ActivityManager.getService();
             if (am != null) {
-                try {
-                    am.killUid(appId, userId, reason);
-                } catch (RemoteException e) {
-                    /* ignore - same process */
-                }
+                am.killUid(appId, UserHandle.USER_ALL, "PlatformCompat overrides");
             }
+        } catch (RemoteException e) {
+            /* ignore - same process */
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -350,13 +324,12 @@
         if (Binder.getCallingUid() == SYSTEM_UID) {
             return;
         }
-        if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE)
-                != PERMISSION_GRANTED) {
+        if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) != PERMISSION_GRANTED) {
             throw new SecurityException("Cannot log compat change usage");
         }
     }
 
-    private void checkCompatChangeReadPermission() throws SecurityException {
+    private void checkCompatChangeReadPermission() {
         // Don't check for permissions within the system process
         if (Binder.getCallingUid() == SYSTEM_UID) {
             return;
@@ -367,7 +340,7 @@
         }
     }
 
-    private void checkCompatChangeOverridePermission() throws SecurityException {
+    private void checkCompatChangeOverridePermission() {
         // Don't check for permissions within the system process
         if (Binder.getCallingUid() == SYSTEM_UID) {
             return;
@@ -378,7 +351,7 @@
         }
     }
 
-    private void checkCompatChangeReadAndLogPermission() throws SecurityException {
+    private void checkCompatChangeReadAndLogPermission() {
         checkCompatChangeReadPermission();
         checkCompatChangeLogPermission();
     }
@@ -391,16 +364,34 @@
             return false;
         }
         if (change.getEnableSinceTargetSdk() > 0) {
-            if (change.getEnableSinceTargetSdk() < sMinTargetSdk) {
-                return false;
-            }
+            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
         }
         return true;
     }
 
     /**
+     * Registers a listener for change state overrides.
+     *
+     * <p>Only one listener per change is allowed.
+     *
+     * <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with
+     * packageName before the app is killed upon an override change. The state of a change is not
+     * guaranteed to change when {@code listener.onCompatChange(String)} is called.
+     *
+     * @param changeId to get updates for
+     * @param listener the listener that will be called upon a potential change for package
+     * @return {@code true} if a change with changeId was already known, or (@code false}
+     * otherwise
+     * @throws IllegalStateException if a listener was already registered for changeId
+     */
+    public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
+        return mCompatConfig.registerListener(changeId, listener);
+    }
+
+    /**
      * Registers a broadcast receiver that listens for package install, replace or remove.
-     * @param context the context where the receiver should be registered.
+     *
+     * @param context the context where the receiver should be registered
      */
     public void registerPackageReceiver(Context context) {
         final BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -429,8 +420,8 @@
     }
 
     /**
-     * Register the observer for
-     * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}
+     * Registers the observer for
+     * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}.
      */
     public void registerContentObserver() {
         mCompatConfig.registerContentObserver();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 16695d1..e854481 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -291,6 +291,8 @@
                 return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
                 return STORAGE_GLOBAL_SETTINGS;
+            case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
+                return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
                 return STORAGE_SHARED_PREFS;
             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
@@ -309,6 +311,8 @@
                 return Global.HDMI_CEC_VERSION;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
                 return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
+            case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
+                return Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
                 return setting.getName();
             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
@@ -323,13 +327,13 @@
         @Storage int storage = getStorage(setting);
         String storageKey = getStorageKey(setting);
         if (storage == STORAGE_SYSPROPS) {
-            Slog.d(TAG, "Reading '" + storageKey + "' sysprop.");
+            HdmiLogger.debug("Reading '" + storageKey + "' sysprop.");
             return mStorageAdapter.retrieveSystemProperty(storageKey, defaultValue);
         } else if (storage == STORAGE_GLOBAL_SETTINGS) {
-            Slog.d(TAG, "Reading '" + storageKey + "' global setting.");
+            HdmiLogger.debug("Reading '" + storageKey + "' global setting.");
             return mStorageAdapter.retrieveGlobalSetting(storageKey, defaultValue);
         } else if (storage == STORAGE_SHARED_PREFS) {
-            Slog.d(TAG, "Reading '" + storageKey + "' shared preference.");
+            HdmiLogger.debug("Reading '" + storageKey + "' shared preference.");
             return mStorageAdapter.retrieveSharedPref(storageKey, defaultValue);
         }
         return null;
@@ -339,13 +343,13 @@
         @Storage int storage = getStorage(setting);
         String storageKey = getStorageKey(setting);
         if (storage == STORAGE_SYSPROPS) {
-            Slog.d(TAG, "Setting '" + storageKey + "' sysprop.");
+            HdmiLogger.debug("Setting '" + storageKey + "' sysprop.");
             mStorageAdapter.storeSystemProperty(storageKey, value);
         } else if (storage == STORAGE_GLOBAL_SETTINGS) {
-            Slog.d(TAG, "Setting '" + storageKey + "' global setting.");
+            HdmiLogger.debug("Setting '" + storageKey + "' global setting.");
             mStorageAdapter.storeGlobalSetting(storageKey, value);
         } else if (storage == STORAGE_SHARED_PREFS) {
-            Slog.d(TAG, "Setting '" + storageKey + "' shared pref.");
+            HdmiLogger.debug("Setting '" + storageKey + "' shared pref.");
             mStorageAdapter.storeSharedPref(storageKey, value);
             notifySettingChanged(setting);
         }
@@ -366,6 +370,9 @@
             case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP:
                 notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
                 break;
+            case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
+                notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE);
+                break;
         }
     }
 
@@ -399,6 +406,7 @@
                 Global.HDMI_CONTROL_ENABLED,
                 Global.HDMI_CEC_VERSION,
                 Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+                Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
         };
         for (String setting: settings) {
             resolver.registerContentObserver(Global.getUriFor(setting), false,
@@ -597,7 +605,7 @@
             throw new IllegalArgumentException("Setting '" + name
                     + "' is not a string-type setting.");
         }
-        Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+        HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
         return retrieveValue(setting, setting.getDefaultValue().getStringValue());
     }
 
@@ -613,7 +621,7 @@
             throw new IllegalArgumentException("Setting '" + name
                     + "' is not a int-type setting.");
         }
-        Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+        HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
         String defaultValue = Integer.toString(getIntValue(setting.getDefaultValue()));
         String value = retrieveValue(setting, defaultValue);
         return Integer.parseInt(value);
@@ -638,7 +646,7 @@
             throw new IllegalArgumentException("Invalid CEC setting '" + name
                                                + "' value: '" + value + "'.");
         }
-        Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+        HdmiLogger.debug("Updating CEC setting '" + name + "' to '" + value + "'.");
         storeValue(setting, value);
     }
 
@@ -661,7 +669,7 @@
             throw new IllegalArgumentException("Invalid CEC setting '" + name
                                                + "' value: '" + value + "'.");
         }
-        Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+        HdmiLogger.debug("Updating CEC setting '" + name + "' to '" + value + "'.");
         storeValue(setting, Integer.toString(value));
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 0b10cc3..ccce9dc 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -619,7 +619,9 @@
         } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
             mService.wakeUp();
             return true;
-        } else if (!mService.isHdmiCecVolumeControlEnabled() && isVolumeOrMuteCommand(message)) {
+        } else if (mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED && isVolumeOrMuteCommand(
+                message)) {
             return false;
         }
 
@@ -1142,7 +1144,8 @@
     @ServiceThreadOnly
     protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) {
         assertRunOnServiceThread();
-        if (!mService.isHdmiCecVolumeControlEnabled()) {
+        if (mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return;
         }
         if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index a945c90..b909b16 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -373,7 +373,8 @@
     @ServiceThreadOnly
     protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (isSystemAudioControlFeatureEnabled() && mService.isHdmiCecVolumeControlEnabled()) {
+        if (isSystemAudioControlFeatureEnabled() && mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_ENABLED) {
             reportAudioStatus(message.getSource());
         } else {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
@@ -723,7 +724,8 @@
 
     void reportAudioStatus(int source) {
         assertRunOnServiceThread();
-        if (!mService.isHdmiCecVolumeControlEnabled()) {
+        if (mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index ad3773e..e568aa2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -618,7 +618,8 @@
     @ServiceThreadOnly
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (!mService.isHdmiCecVolumeControlEnabled()) {
+        if (mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return false;
         }
 
@@ -897,7 +898,8 @@
     }
 
     void setAudioStatus(boolean mute, int volume) {
-        if (!isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) {
+        if (!isSystemAudioActivated() || mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return;
         }
         synchronized (mLock) {
@@ -919,7 +921,8 @@
             // On initialization process, getAvrDeviceInfo() may return null and cause exception
             return;
         }
-        if (delta == 0 || !isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) {
+        if (delta == 0 || !isSystemAudioActivated() || mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return;
         }
 
@@ -948,7 +951,8 @@
     @ServiceThreadOnly
     void changeMute(boolean mute) {
         assertRunOnServiceThread();
-        if (getAvrDeviceInfo() == null || !mService.isHdmiCecVolumeControlEnabled()) {
+        if (getAvrDeviceInfo() == null || mService.getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             // On initialization process, getAvrDeviceInfo() may return null and cause exception
             return;
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b427bd0..9b194ae 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
+import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
 
 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
 import static com.android.server.hdmi.Constants.DISABLED;
@@ -206,7 +207,8 @@
 
     // Whether HDMI CEC volume control is enabled or not.
     @GuardedBy("mLock")
-    private boolean mHdmiCecVolumeControlEnabled;
+    @HdmiControlManager.VolumeControl
+    private int mHdmiCecVolumeControl;
 
     // Make sure HdmiCecConfig is instantiated and the XMLs are read.
     private final HdmiCecConfig mHdmiCecConfig;
@@ -324,7 +326,8 @@
     // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
     // handling will be disabled and no request will be handled.
     @GuardedBy("mLock")
-    private boolean mHdmiControlEnabled;
+    @HdmiControlManager.HdmiCecControl
+    private int mHdmiControlEnabled;
 
     // Set to true while the service is in normal mode. While set to false, no input change is
     // allowed. Used for situations where input change can confuse users such as channel auto-scan,
@@ -476,10 +479,9 @@
         mPowerStatusController.setPowerStatus(getInitialPowerStatus());
         mProhibitMode = false;
         mHdmiControlEnabled = mHdmiCecConfig.getIntValue(
-                                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)
-                                    == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
-        mHdmiCecVolumeControlEnabled = readBooleanSetting(
-                Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true);
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+        setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
         mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
 
         if (mCecController == null) {
@@ -496,7 +498,7 @@
             Slog.i(TAG, "Device does not support MHL-control.");
         }
         mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
-        if (mHdmiControlEnabled) {
+        if (mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
             initializeCec(INITIATED_BY_BOOT_UP);
         } else {
             mCecController.setOption(OptionKey.ENABLE_CEC, false);
@@ -512,9 +514,8 @@
                 new HdmiCecConfig.SettingChangeListener() {
                     @Override
                     public void onChange(String setting) {
-                        boolean enabled = mHdmiCecConfig.getIntValue(
-                                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)
-                                    == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+                        @HdmiControlManager.HdmiCecControl int enabled = mHdmiCecConfig.getIntValue(
+                                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
                         setControlEnabled(enabled);
                     }
                 });
@@ -629,7 +630,7 @@
         }
         if (reason != -1) {
             invokeVendorCommandListenersOnControlStateChanged(true, reason);
-            announceHdmiControlStatusChange(true);
+            announceHdmiControlStatusChange(HDMI_CEC_CONTROL_ENABLED);
         }
     }
 
@@ -676,7 +677,8 @@
             boolean enabled = readBooleanSetting(option, true);
             switch (option) {
                 case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
-                    setHdmiCecVolumeControlEnabledInternal(enabled);
+                    setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+                            HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
                     break;
                 case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
                     if (isTvDeviceEnabled()) {
@@ -1212,7 +1214,8 @@
     void setAudioStatus(boolean mute, int volume) {
         if (!isTvDeviceEnabled()
                 || !tv().isSystemAudioActivated()
-                || !isHdmiCecVolumeControlEnabled()) {
+                || getHdmiCecVolumeControl()
+                == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
             return;
         }
         AudioManager audioManager = getAudioManager();
@@ -2120,24 +2123,6 @@
         }
 
         @Override
-        public boolean isHdmiCecVolumeControlEnabled() {
-            initBinderCall();
-            return HdmiControlService.this.isHdmiCecVolumeControlEnabled();
-        }
-
-        @Override
-        public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) {
-            initBinderCall();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                HdmiControlService.this.setHdmiCecVolumeControlEnabled(
-                        isHdmiCecVolumeControlEnabled);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
         public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
                 final boolean isMute) {
             initBinderCall();
@@ -2208,10 +2193,9 @@
             // System settings
             pw.println("System_settings:");
             pw.increaseIndent();
-            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
             pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
             pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
-            pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
+            pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControl);
             pw.decreaseIndent();
 
             // CEC settings
@@ -2340,6 +2324,13 @@
         }
     }
 
+    @VisibleForTesting
+    void setHdmiCecVolumeControlEnabledInternal(
+            @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
+        mHdmiCecVolumeControl = hdmiCecVolumeControl;
+        announceHdmiCecVolumeControlFeatureChange(hdmiCecVolumeControl);
+    }
+
     // Get the source address to send out commands to devices connected to the current device
     // when other services interact with HdmiControlService.
     private int getRemoteControlSourceAddress() {
@@ -2533,10 +2524,10 @@
                 // Return the current status of mHdmiCecVolumeControlEnabled;
                 synchronized (mLock) {
                     try {
-                        listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControlEnabled);
+                        listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControl);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: "
-                                + mHdmiCecVolumeControlEnabled, e);
+                                + mHdmiCecVolumeControl, e);
                     }
                 }
             }
@@ -2781,7 +2772,7 @@
         }
     }
 
-    private void announceHdmiControlStatusChange(boolean isEnabled) {
+    private void announceHdmiControlStatusChange(@HdmiControlManager.HdmiCecControl int isEnabled) {
         assertRunOnServiceThread();
         synchronized (mLock) {
             List<IHdmiControlStatusChangeListener> listeners = new ArrayList<>(
@@ -2795,16 +2786,18 @@
     }
 
     private void invokeHdmiControlStatusChangeListenerLocked(
-            IHdmiControlStatusChangeListener listener, boolean isEnabled) {
+            IHdmiControlStatusChangeListener listener,
+            @HdmiControlManager.HdmiCecControl int isEnabled) {
         invokeHdmiControlStatusChangeListenerLocked(Collections.singletonList(listener), isEnabled);
     }
 
     private void invokeHdmiControlStatusChangeListenerLocked(
-            Collection<IHdmiControlStatusChangeListener> listeners, boolean isEnabled) {
+            Collection<IHdmiControlStatusChangeListener> listeners,
+            @HdmiControlManager.HdmiCecControl int isEnabled) {
         if (listeners.isEmpty()) {
             return;
         }
-        if (isEnabled) {
+        if (isEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
             queryDisplayStatus(new IHdmiControlCallback.Stub() {
                 public void onComplete(int status) {
                     boolean isAvailable = true;
@@ -2822,7 +2815,8 @@
     }
 
     private void invokeHdmiControlStatusChangeListenerLocked(
-            Collection<IHdmiControlStatusChangeListener> listeners, boolean isEnabled,
+            Collection<IHdmiControlStatusChangeListener> listeners,
+            @HdmiControlManager.HdmiCecControl int isEnabled,
             boolean isCecAvailable) {
         for (IHdmiControlStatusChangeListener listener : listeners) {
             try {
@@ -2835,15 +2829,16 @@
         }
     }
 
-    private void announceHdmiCecVolumeControlFeatureChange(boolean isEnabled) {
+    private void announceHdmiCecVolumeControlFeatureChange(
+            @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
         assertRunOnServiceThread();
         mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
             try {
-                listener.onHdmiCecVolumeControlFeature(isEnabled);
+                listener.onHdmiCecVolumeControlFeature(hdmiCecVolumeControl);
             } catch (RemoteException e) {
                 Slog.e(TAG,
                         "Failed to report HdmiControlVolumeControlStatusChange: "
-                                + isEnabled);
+                                + hdmiCecVolumeControl);
             }
         });
     }
@@ -2888,7 +2883,7 @@
 
     boolean isControlEnabled() {
         synchronized (mLock) {
-            return mHdmiControlEnabled;
+            return mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
         }
     }
 
@@ -2962,7 +2957,7 @@
         mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON,
                 false);
         if (mCecController != null) {
-            if (mHdmiControlEnabled) {
+            if (mHdmiControlEnabled == HDMI_CEC_CONTROL_ENABLED) {
                 int startReason = -1;
                 switch (wakeUpAction) {
                     case WAKE_UP_SCREEN_ON:
@@ -3193,31 +3188,10 @@
         }
     }
 
-    void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
-        setHdmiCecVolumeControlEnabledInternal(isHdmiCecVolumeControlEnabled);
-
-        writeBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
-                isHdmiCecVolumeControlEnabled);
-    }
-
-    @VisibleForTesting
-    void setHdmiCecVolumeControlEnabledInternal(boolean isHdmiCecVolumeControlEnabled) {
+    @HdmiControlManager.VolumeControl
+    int getHdmiCecVolumeControl() {
         synchronized (mLock) {
-            mHdmiCecVolumeControlEnabled = isHdmiCecVolumeControlEnabled;
-
-            boolean storedValue = readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
-                    true);
-            if (storedValue != isHdmiCecVolumeControlEnabled) {
-                HdmiLogger.debug("Changing HDMI CEC volume control feature state: %s",
-                        isHdmiCecVolumeControlEnabled);
-            }
-        }
-        announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled);
-    }
-
-    boolean isHdmiCecVolumeControlEnabled() {
-        synchronized (mLock) {
-            return mHdmiCecVolumeControlEnabled;
+            return mHdmiCecVolumeControl;
         }
     }
 
@@ -3252,21 +3226,21 @@
     }
 
     @ServiceThreadOnly
-    void setControlEnabled(boolean enabled) {
+    void setControlEnabled(@HdmiControlManager.HdmiCecControl int enabled) {
         assertRunOnServiceThread();
 
         synchronized (mLock) {
             mHdmiControlEnabled = enabled;
         }
 
-        if (enabled) {
+        if (enabled == HDMI_CEC_CONTROL_ENABLED) {
             enableHdmiControlService();
-            setHdmiCecVolumeControlEnabledInternal(
-                    readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true));
+            setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
             return;
         }
 
-        setHdmiCecVolumeControlEnabledInternal(false);
+        setHdmiCecVolumeControlEnabledInternal(HdmiControlManager.VOLUME_CONTROL_DISABLED);
         // Call the vendor handler before the service is disabled.
         invokeVendorCommandListenersOnControlStateChanged(false,
                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml
index 480e0ec..e6e3c2f 100644
--- a/services/core/java/com/android/server/hdmi/cec_config.xml
+++ b/services/core/java/com/android/server/hdmi/cec_config.xml
@@ -46,4 +46,13 @@
     </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>
 </cec-settings>
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ab7e3b0..143ec15 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -167,6 +167,7 @@
 import com.android.internal.inputmethod.IInputMethodSubtypeListResultCallback;
 import com.android.internal.inputmethod.IInputMethodSubtypeResultCallback;
 import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.IVoidResultCallback;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
@@ -3720,38 +3721,43 @@
     }
 
     @Override
-    public void showInputMethodPickerFromClient(
-            IInputMethodClient client, int auxiliarySubtypeMode) {
-        synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
-                return;
-            }
-            if(!canShowInputMethodPickerLocked(client)) {
-                Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
-                        + Binder.getCallingUid() + ": " + client);
-                return;
-            }
+    public void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode,
+            IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            synchronized (mMethodMap) {
+                if (!calledFromValidUserLocked()) {
+                    return;
+                }
+                if (!canShowInputMethodPickerLocked(client)) {
+                    Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
+                            + Binder.getCallingUid() + ": " + client);
+                    return;
+                }
 
-            // Always call subtype picker, because subtype picker is a superset of input method
-            // picker.
-            mHandler.sendMessage(mCaller.obtainMessageII(
-                    MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode,
-                    (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY));
-        }
+                // Always call subtype picker, because subtype picker is a superset of input method
+                // picker.
+                mHandler.sendMessage(mCaller.obtainMessageII(
+                        MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode,
+                        (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY));
+            }
+        });
     }
 
     @Override
     public void showInputMethodPickerFromSystem(IInputMethodClient client, int auxiliarySubtypeMode,
-            int displayId) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException(
-                    "showInputMethodPickerFromSystem requires WRITE_SECURE_SETTINGS permission");
-        }
-        // Always call subtype picker, because subtype picker is a superset of input method
-        // picker.
-        mHandler.sendMessage(mCaller.obtainMessageII(
-                MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId));
+            int displayId, IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            if (mContext.checkCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "showInputMethodPickerFromSystem requires WRITE_SECURE_SETTINGS "
+                                + "permission");
+            }
+            // Always call subtype picker, because subtype picker is a superset of input method
+            // picker.
+            mHandler.sendMessage(mCaller.obtainMessageII(
+                    MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId));
+        });
     }
 
     /**
@@ -3795,15 +3801,17 @@
 
     @Override
     public void showInputMethodAndSubtypeEnablerFromClient(
-            IInputMethodClient client, String inputMethodId) {
-        synchronized (mMethodMap) {
-            // TODO(yukawa): Should we verify the display ID?
-            if (!calledFromValidUserLocked()) {
-                return;
+            IInputMethodClient client, String inputMethodId, IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            synchronized (mMethodMap) {
+                // TODO(yukawa): Should we verify the display ID?
+                if (!calledFromValidUserLocked()) {
+                    return;
+                }
+                executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
+                        MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
             }
-            executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
-                    MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
-        }
+        });
     }
 
     @BinderThread
@@ -4011,84 +4019,87 @@
 
     @Override
     public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
-            float[] matrixValues) {
-        final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
-        if (displayInfo == null) {
-            throw new IllegalArgumentException(
-                    "Cannot find display for non-existent displayId: " + childDisplayId);
-        }
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != displayInfo.ownerUid) {
-            throw new SecurityException("The caller doesn't own the display.");
-        }
-
-        synchronized (mMethodMap) {
-            final ClientState cs = mClients.get(parentClient.asBinder());
-            if (cs == null) {
-                return;
+            float[] matrixValues, IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
+            if (displayInfo == null) {
+                throw new IllegalArgumentException(
+                        "Cannot find display for non-existent displayId: " + childDisplayId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid != displayInfo.ownerUid) {
+                throw new SecurityException("The caller doesn't own the display.");
             }
 
-            // null matrixValues means that the entry needs to be removed.
-            if (matrixValues == null) {
-                final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
-                if (info == null) {
+            synchronized (mMethodMap) {
+                final ClientState cs = mClients.get(parentClient.asBinder());
+                if (cs == null) {
                     return;
                 }
-                if (info.mParentClient != cs) {
-                    throw new SecurityException("Only the owner client can clear"
-                            + " ActivityViewGeometry for display #" + childDisplayId);
-                }
-                mActivityViewDisplayIdToParentMap.remove(childDisplayId);
-                return;
-            }
 
-            ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
-            if (info != null && info.mParentClient != cs) {
-                throw new InvalidParameterException("Display #" + childDisplayId
-                        + " is already registered by " + info.mParentClient);
-            }
-            if (info == null) {
-                if (!mWindowManagerInternal.isUidAllowedOnDisplay(childDisplayId, cs.uid)) {
-                    throw new SecurityException(cs + " cannot access to display #"
-                            + childDisplayId);
-                }
-                info = new ActivityViewInfo(cs, new Matrix());
-                mActivityViewDisplayIdToParentMap.put(childDisplayId, info);
-            }
-            info.mMatrix.setValues(matrixValues);
-
-            if (mCurClient == null || mCurClient.curSession == null) {
-                return;
-            }
-
-            Matrix matrix = null;
-            int displayId = mCurClient.selfReportedDisplayId;
-            boolean needToNotify = false;
-            while (true) {
-                needToNotify |= (displayId == childDisplayId);
-                final ActivityViewInfo next = mActivityViewDisplayIdToParentMap.get(displayId);
-                if (next == null) {
-                    break;
-                }
-                if (matrix == null) {
-                    matrix = new Matrix(next.mMatrix);
-                } else {
-                    matrix.postConcat(next.mMatrix);
-                }
-                if (next.mParentClient.selfReportedDisplayId == mCurTokenDisplayId) {
-                    if (needToNotify) {
-                        final float[] values = new float[9];
-                        matrix.getValues(values);
-                        try {
-                            mCurClient.client.updateActivityViewToScreenMatrix(mCurSeq, values);
-                        } catch (RemoteException e) {
-                        }
+                // null matrixValues means that the entry needs to be removed.
+                if (matrixValues == null) {
+                    final ActivityViewInfo info =
+                            mActivityViewDisplayIdToParentMap.get(childDisplayId);
+                    if (info == null) {
+                        return;
                     }
-                    break;
+                    if (info.mParentClient != cs) {
+                        throw new SecurityException("Only the owner client can clear"
+                                + " ActivityViewGeometry for display #" + childDisplayId);
+                    }
+                    mActivityViewDisplayIdToParentMap.remove(childDisplayId);
+                    return;
                 }
-                displayId = info.mParentClient.selfReportedDisplayId;
+
+                ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
+                if (info != null && info.mParentClient != cs) {
+                    throw new InvalidParameterException("Display #" + childDisplayId
+                            + " is already registered by " + info.mParentClient);
+                }
+                if (info == null) {
+                    if (!mWindowManagerInternal.isUidAllowedOnDisplay(childDisplayId, cs.uid)) {
+                        throw new SecurityException(cs + " cannot access to display #"
+                                + childDisplayId);
+                    }
+                    info = new ActivityViewInfo(cs, new Matrix());
+                    mActivityViewDisplayIdToParentMap.put(childDisplayId, info);
+                }
+                info.mMatrix.setValues(matrixValues);
+
+                if (mCurClient == null || mCurClient.curSession == null) {
+                    return;
+                }
+
+                Matrix matrix = null;
+                int displayId = mCurClient.selfReportedDisplayId;
+                boolean needToNotify = false;
+                while (true) {
+                    needToNotify |= (displayId == childDisplayId);
+                    final ActivityViewInfo next = mActivityViewDisplayIdToParentMap.get(displayId);
+                    if (next == null) {
+                        break;
+                    }
+                    if (matrix == null) {
+                        matrix = new Matrix(next.mMatrix);
+                    } else {
+                        matrix.postConcat(next.mMatrix);
+                    }
+                    if (next.mParentClient.selfReportedDisplayId == mCurTokenDisplayId) {
+                        if (needToNotify) {
+                            final float[] values = new float[9];
+                            matrix.getValues(values);
+                            try {
+                                mCurClient.client.updateActivityViewToScreenMatrix(mCurSeq, values);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                        break;
+                    }
+                    displayId = info.mParentClient.selfReportedDisplayId;
+                }
             }
-        }
+        });
     }
 
     @Override
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 3485881..2dd7096 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -82,6 +82,7 @@
 import com.android.internal.inputmethod.IMultiClientInputMethod;
 import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+import com.android.internal.inputmethod.IVoidResultCallback;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
@@ -1776,23 +1777,26 @@
 
         @BinderThread
         @Override
-        public void showInputMethodPickerFromClient(
-                IInputMethodClient client, int auxiliarySubtypeMode) {
+        public void showInputMethodPickerFromClient(IInputMethodClient client,
+                int auxiliarySubtypeMode, IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
         @Override
-        public void showInputMethodPickerFromSystem(
-                IInputMethodClient client, int auxiliarySubtypeMode, int displayId) {
+        public void showInputMethodPickerFromSystem(IInputMethodClient client,
+                int auxiliarySubtypeMode, int displayId, IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
         @Override
-        public void showInputMethodAndSubtypeEnablerFromClient(
-                IInputMethodClient client, String inputMethodId) {
+        public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient client,
+                String inputMethodId, IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
@@ -1825,8 +1829,9 @@
         @BinderThread
         @Override
         public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
-                float[] matrixValues) {
+                float[] matrixValues, IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 83b6eca..b5b93d6 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -949,7 +949,8 @@
     }
 
     @Override
-    public boolean isProviderPackage(String provider, String packageName) {
+    public boolean isProviderPackage(@Nullable String provider, String packageName,
+            @Nullable String attributionTag) {
         mContext.enforceCallingOrSelfPermission(permission.READ_DEVICE_CONFIG, null);
 
         for (LocationProviderManager manager : mProviderManagers) {
@@ -960,7 +961,8 @@
             if (identity == null) {
                 continue;
             }
-            if (identity.getPackageName().equals(packageName)) {
+            if (identity.getPackageName().equals(packageName) && (attributionTag == null
+                    || Objects.equals(identity.getAttributionTag(), attributionTag))) {
                 return true;
             }
         }
@@ -1262,13 +1264,17 @@
         }
 
         @Override
-        public boolean isProvider(String provider, CallerIdentity identity) {
-            LocationProviderManager manager = getLocationProviderManager(provider);
-            if (manager == null) {
-                return false;
-            } else {
-                return identity.equals(manager.getIdentity());
+        public boolean isProvider(@Nullable String provider, CallerIdentity identity) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                if (provider != null && !provider.equals(manager.getName())) {
+                    continue;
+                }
+                if (identity.equalsIgnoringListenerId(manager.getIdentity())) {
+                    return true;
+                }
             }
+
+            return false;
         }
 
         @Override
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 b06389a..06ca9ec 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1333,9 +1333,6 @@
                 Binder.restoreCallingIdentity(identity);
             }
 
-            setRealProvider(null);
-            setMockProvider(null);
-
             mUserHelper.removeListener(mUserChangedListener);
             mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9f351a3..28c90e9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -88,6 +88,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
+import android.security.Authorization;
 import android.security.KeyStore;
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
@@ -1268,6 +1269,7 @@
 
     private void unlockKeystore(byte[] password, int userHandle) {
         if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
+        new Authorization().onLockScreenEvent(false, userHandle, password);
         // TODO(b/120484642): Update keystore to accept byte[] passwords
         String passwordString = password == null ? null : new String(password);
         final KeyStore ks = KeyStore.getInstance();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 61b218c..c397724 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -998,6 +998,7 @@
                         REASON_CLICK, nv.rank, nv.count, null);
                 nv.recycle();
                 reportUserInteraction(r);
+                mAssistants.notifyAssistantNotificationClicked(r);
             }
         }
 
@@ -3085,8 +3086,23 @@
 
             synchronized (mToastQueue) {
                 int uid = Binder.getCallingUid();
+                int userId = UserHandle.getUserId(uid);
                 if (enable) {
                     mToastRateLimitingDisabledUids.remove(uid);
+                    try {
+                        String[] packages = mPackageManager.getPackagesForUid(uid);
+                        if (packages == null) {
+                            Slog.e(TAG, "setToastRateLimitingEnabled method haven't found any "
+                                    + "packages for the  given uid: " + uid + ", toast rate "
+                                    + "limiter not reset for that uid.");
+                            return;
+                        }
+                        for (String pkg : packages) {
+                            mToastRateLimiter.clear(userId, pkg);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Failed to reset toast rate limiter for given uid", e);
+                    }
                 } else {
                     mToastRateLimitingDisabledUids.add(uid);
                 }
@@ -9459,6 +9475,22 @@
                     });
         }
 
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantNotificationClicked(final NotificationRecord r) {
+            final String key = r.getSbn().getKey();
+            notifyAssistantLocked(
+                    r.getSbn(),
+                    r.getNotificationType(),
+                    true /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationClicked(key);
+                        } catch (RemoteException ex) {
+                            Slog.e(TAG, "unable to notify assistant (clicked): " + assistant, ex);
+                        }
+                    });
+        }
+
         /**
          * Notifies the assistant something about the specified notification, only assistant
          * that is visible to the notification will be notified.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c93127d..e7a04ef 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15071,6 +15071,16 @@
         }
         final IActivityManager am = ActivityManager.getService();
         try {
+            long duration = 10_000;
+            final ActivityManagerInternal amInternal =
+                    LocalServices.getService(ActivityManagerInternal.class);
+            if (amInternal != null) {
+                duration = amInternal.getBootTimeTempAllowListDuration();
+            }
+            final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+            bOptions.setTemporaryAppWhitelistDuration(
+                        BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                        duration);
             // Deliver LOCKED_BOOT_COMPLETED first
             Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
                     .setPackage(packageName);
@@ -15079,7 +15089,8 @@
             }
             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
             am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
-                    requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                    requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(),
+                    false, false,
                     userId);
 
             // Deliver BOOT_COMPLETED only if user is unlocked
@@ -15089,7 +15100,8 @@
                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                 }
                 am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
-                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                        requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(),
+                        false, false,
                         userId);
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/textservices/OWNERS b/services/core/java/com/android/server/textservices/OWNERS
new file mode 100644
index 0000000..9fa9b29
--- /dev/null
+++ b/services/core/java/com/android/server/textservices/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 816455
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java b/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
new file mode 100644
index 0000000..fb75ae1
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
@@ -0,0 +1,203 @@
+/*
+ * 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.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.timedetector.GnssTimeSuggestion;
+import android.app.timedetector.TimeDetector;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.LocationRequest;
+import android.location.LocationTime;
+import android.os.Binder;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.time.Duration;
+
+/**
+ * Monitors the GNSS time.
+ *
+ * <p>When available, the time is always suggested to the {@link
+ * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
+ * system clock, depending on user settings and what other signals are available.
+ */
+public final class GnssTimeUpdateService extends Binder {
+    private static final String TAG = "GnssTimeUpdateService";
+    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * Handles the lifecycle events for the GnssTimeUpdateService.
+     */
+    public static class Lifecycle extends SystemService {
+        private GnssTimeUpdateService mService;
+
+        public Lifecycle(@NonNull Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new GnssTimeUpdateService(getContext());
+            publishBinderService("gnss_time_update_service", mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            // Need to wait for some location providers to be enabled. If done at
+            // PHASE_SYSTEM_SERVICES_READY, error where "gps" provider does not exist could occur.
+            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+                // Initiate location updates. On boot, GNSS might not be available right away.
+                // Instead of polling GNSS time periodically, passive location updates are enabled.
+                // Once an update is received, the gnss time will be queried and suggested to
+                // TimeDetectorService.
+                mService.requestGnssTimeUpdates();
+            }
+        }
+    }
+
+    private static final Duration GNSS_TIME_UPDATE_ALARM_INTERVAL = Duration.ofHours(4);
+    private static final String ATTRIBUTION_TAG = "GnssTimeUpdateService";
+
+    private final Context mContext;
+    private final TimeDetector mTimeDetector;
+    private final AlarmManager mAlarmManager;
+    private final LocationManager mLocationManager;
+    private final LocationManagerInternal mLocationManagerInternal;
+
+    @Nullable private AlarmManager.OnAlarmListener mAlarmListener;
+    @Nullable private LocationListener mLocationListener;
+    @Nullable private TimestampedValue<Long> mLastSuggestedGnssTime;
+
+    @VisibleForTesting
+    GnssTimeUpdateService(@NonNull Context context) {
+        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
+        mTimeDetector = mContext.getSystemService(TimeDetector.class);
+        mLocationManager = mContext.getSystemService(LocationManager.class);
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
+    }
+
+    /**
+     * Request passive location updates. Such a request will not trigger any active locations or
+     * power usage itself.
+     */
+    @VisibleForTesting
+    void requestGnssTimeUpdates() {
+        if (D) {
+            Log.d(TAG, "requestGnssTimeUpdates()");
+        }
+
+        // Location Listener triggers onLocationChanged() when GNSS data is available, so
+        // that the getGnssTimeMillis() function doesn't need to be continuously polled.
+        mLocationListener = new LocationListener() {
+            @Override
+            public void onLocationChanged(Location location) {
+                if (D) {
+                    Log.d(TAG, "onLocationChanged()");
+                }
+
+                // getGnssTimeMillis() can return null when the Master Location Switch for the
+                // foreground user is disabled.
+                LocationTime locationTime = mLocationManagerInternal.getGnssTimeMillis();
+                if (locationTime != null) {
+                    suggestGnssTime(locationTime);
+                } else {
+                    if (D) {
+                        Log.d(TAG, "getGnssTimeMillis() returned null");
+                    }
+                }
+
+                mLocationManager.removeUpdates(mLocationListener);
+                mLocationListener = null;
+
+                mAlarmListener = new AlarmManager.OnAlarmListener() {
+                    @Override
+                    public void onAlarm() {
+                        if (D) {
+                            Log.d(TAG, "onAlarm()");
+                        }
+                        mAlarmListener = null;
+                        requestGnssTimeUpdates();
+                    }
+                };
+
+                // Set next alarm to re-enable location updates.
+                long next = SystemClock.elapsedRealtime()
+                        + GNSS_TIME_UPDATE_ALARM_INTERVAL.toMillis();
+                mAlarmManager.set(
+                        AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        next,
+                        TAG,
+                        mAlarmListener,
+                        FgThread.getHandler());
+            }
+        };
+
+        mLocationManager.requestLocationUpdates(
+                LocationManager.GPS_PROVIDER,
+                new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL)
+                        .setMinUpdateIntervalMillis(0)
+                        .build(),
+                FgThread.getExecutor(),
+                mLocationListener);
+    }
+
+    /**
+     * Convert LocationTime to TimestampedValue. Then suggest TimestampedValue to Time Detector.
+     */
+    private void suggestGnssTime(LocationTime locationTime) {
+        if (D) {
+            Log.d(TAG, "suggestGnssTime()");
+        }
+        long gnssTime = locationTime.getTime();
+        long elapsedRealtimeMs = locationTime.getElapsedRealtimeNanos() / 1_000_000L;
+
+        TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+                elapsedRealtimeMs, gnssTime);
+        mLastSuggestedGnssTime = timeSignal;
+
+        GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal);
+        mTimeDetector.suggestGnssTime(timeSuggestion);
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        pw.println("mLastSuggestedGnssTime: " + mLastSuggestedGnssTime);
+        pw.print("state: ");
+        if (mLocationListener != null) {
+            pw.println("time updates enabled");
+        } else {
+            pw.println("alarm enabled");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 0e8fd8f..da3e48a 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -55,6 +55,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.security.Authorization;
 import android.security.KeyStore;
 import android.service.trust.TrustAgentService;
 import android.text.TextUtils;
@@ -188,6 +189,8 @@
     private boolean mTrustAgentsCanRun = false;
     private int mCurrentUser = UserHandle.USER_SYSTEM;
 
+    private Authorization mAuthorizationService;
+
     public TrustManagerService(Context context) {
         super(context);
         mContext = context;
@@ -197,6 +200,7 @@
         mStrongAuthTracker = new StrongAuthTracker(context);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mSettingsObserver = new SettingsObserver(mHandler);
+        mAuthorizationService = new Authorization();
     }
 
     @Override
@@ -699,11 +703,13 @@
         if (changed) {
             dispatchDeviceLocked(userId, locked);
 
+            mAuthorizationService.onLockScreenEvent(locked, userId, null);
             KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
             // Also update the user's profiles who have unified challenge, since they
             // share the same unlocked state (see {@link #isDeviceLocked(int)})
             for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
                 if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
+                    mAuthorizationService.onLockScreenEvent(locked, profileHandle, null);
                     KeyStore.getInstance().onUserLockedStateChanged(profileHandle, locked);
                 }
             }
@@ -1255,6 +1261,7 @@
                         mDeviceLockedForUser.put(userId, locked);
                     }
 
+                    mAuthorizationService.onLockScreenEvent(locked, userId, null);
                     KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
 
                     if (locked) {
diff --git a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
index b77df2d..9bf046c 100644
--- a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
+++ b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
@@ -27,7 +27,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.LongArrayQueue;
 import android.util.Slog;
@@ -294,12 +293,11 @@
 
     @Override
     @GuardedBy("mLock")
-    void handleRemovedAppLocked(String packageName, int uid) {
+    void handleRemovedAppLocked(final int userId, @NonNull String packageName) {
         if (packageName == null) {
             Slog.wtf(TAG, "Told app removed but given null package name.");
             return;
         }
-        final int userId = UserHandle.getUserId(uid);
 
         mEventTimes.delete(userId, packageName);
         mExecutionStatsCache.delete(userId, packageName);
diff --git a/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java
index fdbe4b4..a8daa3b 100644
--- a/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java
+++ b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java
@@ -48,6 +48,7 @@
  * @hide
  */
 public class MultiRateLimiter {
+    private static final String TAG = "MultiRateLimiter";
 
     private static final CountQuotaTracker[] EMPTY_TRACKER_ARRAY = {};
 
@@ -73,6 +74,13 @@
         }
     }
 
+    /** Remove all saved events from the rate limiter for the given app (reset it). */
+    public void clear(int userId, @NonNull String packageName) {
+        synchronized (mLock) {
+            clearLocked(userId, packageName);
+        }
+    }
+
     @GuardedBy("mLock")
     private void noteEventLocked(int userId, @NonNull String packageName, @Nullable String tag) {
         for (CountQuotaTracker quotaTracker : mQuotaTrackers) {
@@ -91,6 +99,16 @@
         return true;
     }
 
+    @GuardedBy("mLock")
+    private void clearLocked(int userId, @NonNull String packageName) {
+        for (CountQuotaTracker quotaTracker : mQuotaTrackers) {
+            // This method behaves as if the package has been removed from the device, which
+            // isn't the case here, but it does similar clean-up to what we are aiming for here,
+            // so it works for this use case.
+            quotaTracker.onAppRemovedLocked(userId, packageName);
+        }
+    }
+
     /** Can create a new {@link MultiRateLimiter}. */
     public static class Builder {
 
@@ -98,7 +116,8 @@
         private final Context mContext;
         private final Categorizer mCategorizer;
         private final Category mCategory;
-        @Nullable private final QuotaTracker.Injector mInjector;
+        @Nullable
+        private final QuotaTracker.Injector mInjector;
 
         /**
          * Creates a new builder and allows to inject an object that can be used
@@ -121,7 +140,7 @@
         /**
          * Adds another rate limit to be used in {@link MultiRateLimiter}.
          *
-         * @param limit The maximum event count an app can have in the rolling time window.
+         * @param limit      The maximum event count an app can have in the rolling time window.
          * @param windowSize The rolling time window to use when checking quota usage.
          */
         public Builder addRateLimit(int limit, Duration windowSize) {
@@ -164,7 +183,7 @@
         public final Duration mWindowSize;
 
         /**
-         * @param limit The maximum count of some occurrence in the rolling time window.
+         * @param limit      The maximum count of some occurrence in the rolling time window.
          * @param windowSize The rolling time window to use when checking quota usage.
          */
         private RateLimit(int limit, Duration windowSize) {
@@ -173,7 +192,7 @@
         }
 
         /**
-         * @param limit The maximum count of some occurrence in the rolling time window.
+         * @param limit      The maximum count of some occurrence in the rolling time window.
          * @param windowSize The rolling time window to use when checking quota usage.
          */
         public static RateLimit create(int limit, Duration windowSize) {
diff --git a/services/core/java/com/android/server/utils/quota/QuotaTracker.java b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
index 7f44618..802ab5b 100644
--- a/services/core/java/com/android/server/utils/quota/QuotaTracker.java
+++ b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
@@ -58,7 +58,7 @@
  * of quota until it is below that limit again. Limits are applied according to the category
  * the UPTC is placed in. Categories are basic constructs to apply different limits to
  * different groups of UPTCs. For example, standby buckets can be a set of categories, or
- * foreground & background could be two categories. If every UPTC should have the limits
+ * foreground & background could be two categories. If every UPTC should have the same limits
  * applied, then only one category is needed.
  *
  * Note: all limits are enforced per category unless explicitly stated otherwise.
@@ -132,7 +132,7 @@
                 case Intent.ACTION_PACKAGE_FULLY_REMOVED:
                     final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                     synchronized (mLock) {
-                        onAppRemovedLocked(getPackageName(intent), uid);
+                        onAppRemovedLocked(UserHandle.getUserId(uid), getPackageName(intent));
                     }
                     break;
                 case Intent.ACTION_USER_REMOVED:
@@ -358,21 +358,20 @@
     }
 
     @GuardedBy("mLock")
-    abstract void handleRemovedAppLocked(String packageName, int uid);
+    abstract void handleRemovedAppLocked(int userId, @NonNull String packageName);
 
     @GuardedBy("mLock")
-    private void onAppRemovedLocked(String packageName, int uid) {
+    void onAppRemovedLocked(final int userId, @NonNull String packageName) {
         if (packageName == null) {
             Slog.wtf(TAG, "Told app removed but given null package name.");
             return;
         }
-        final int userId = UserHandle.getUserId(uid);
 
         mInQuotaAlarmListener.removeAlarmsLocked(userId, packageName);
 
         mFreeQuota.delete(userId, packageName);
 
-        handleRemovedAppLocked(packageName, uid);
+        handleRemovedAppLocked(userId, packageName);
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f9cc334..0f4bb4c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2606,7 +2606,7 @@
 
             finishActivityResults(resultCode, resultData, resultGrants);
 
-            final boolean endTask = task.getActivityBelow(this) == null
+            final boolean endTask = task.getTopNonFinishingActivity() == null
                     && !task.isClearingToReuseTask();
             final int transit = endTask ? TRANSIT_OLD_TASK_CLOSE : TRANSIT_OLD_ACTIVITY_CLOSE;
             if (isState(RESUMED)) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 0fc4cdb..d90e885 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -662,7 +662,9 @@
         pw.println(getName()
                 + " type=" + activityTypeToString(getActivityType())
                 + " mode=" + windowingModeToString(getWindowingMode())
-                + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()));
+                + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode())
+                + " requested-bounds=" + getRequestedOverrideBounds().toShortString()
+                + " bounds=" + getBounds().toShortString());
         for (int i = getChildCount() - 1; i >= 0; --i) {
             final E cc = getChildAt(i);
             pw.print(childPrefix + "#" + i + " ");
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index a20d924..cc77cfa 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -407,7 +407,7 @@
         }
 
         mInputFocus = focusToken;
-        mInputTransaction.setFocusedWindow(mInputFocus, mDisplayId);
+        mInputTransaction.setFocusedWindow(mInputFocus, focus.getName(), mDisplayId);
         EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus,
                 "reason=UpdateInputWindows");
         ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 855c8f5..2295ee3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,6 +29,7 @@
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -433,6 +434,7 @@
 
     CharSequence lastDescription; // Last description captured for this item.
 
+    Task mAdjacentTask; // Task adjacent to this one.
     int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
     Task mPrevAffiliate; // previous task in affiliated chain.
     int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
@@ -1564,6 +1566,11 @@
                 mTaskId, mUserId);
     }
 
+    void setAdjacentTask(Task adjacent) {
+        mAdjacentTask = adjacent;
+        adjacent.mAdjacentTask = this;
+    }
+
     void setTaskToAffiliateWith(Task taskToAffiliateWith) {
         closeRecentsChain();
         mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -4244,6 +4251,7 @@
             }
         }
 
+        final List<Task> adjacentTasks = new ArrayList<>();
         final int windowingMode = getWindowingMode();
         final boolean isAssistantType = isActivityTypeAssistant();
         for (int i = parent.getChildCount() - 1; i >= 0; --i) {
@@ -4273,6 +4281,15 @@
                     continue;
                 }
                 return TASK_VISIBILITY_INVISIBLE;
+            } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+                    && other.matchParentBounds()) {
+                if (other.isTranslucent(starting)) {
+                    // Can be visible behind a translucent task.
+                    gotTranslucentFullscreen = true;
+                    continue;
+                }
+                // Multi-window task that matches parent bounds would occlude other children.
+                return TASK_VISIBILITY_INVISIBLE;
             } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                     && !gotOpaqueSplitScreenPrimary) {
                 gotSplitScreenStack = true;
@@ -4305,6 +4322,20 @@
                 // assistant window surfaces in window manager whenever it is visible.
                 return TASK_VISIBILITY_INVISIBLE;
             }
+            if (other.mAdjacentTask != null) {
+                if (adjacentTasks.contains(other.mAdjacentTask)) {
+                    if (other.isTranslucent(starting)
+                            || other.mAdjacentTask.isTranslucent(starting)) {
+                        // Can be visible behind a translucent adjacent tasks.
+                        gotTranslucentFullscreen = true;
+                        continue;
+                    }
+                    // Can not be visible behind adjacent tasks.
+                    return TASK_VISIBILITY_INVISIBLE;
+                } else {
+                    adjacentTasks.add(other);
+                }
+            }
         }
 
         if (!shouldBeVisible) {
@@ -5443,9 +5474,13 @@
                 final Task lastFocusedTask = displayArea.getFocusedRootTask();
                 displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/);
                 displayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
+                mAtmService.getTaskChangeNotificationController().notifyTaskMovedToBack(
+                        getTaskInfo());
             }
             if (task != null && task != this) {
                 positionChildAtBottom(task);
+                mAtmService.getTaskChangeNotificationController().notifyTaskMovedToBack(
+                        task.getTaskInfo());
             }
             return;
         }
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 28ebe01..58d508d 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.window.TaskSnapshot;
 import android.app.ITaskStackListener;
 import android.app.TaskInfo;
 import android.content.ComponentName;
@@ -29,6 +28,7 @@
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.window.TaskSnapshot;
 
 import com.android.internal.os.SomeArgs;
 
@@ -60,6 +60,7 @@
     private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 25;
     private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 26;
     private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 27;
+    private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 28;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -178,6 +179,10 @@
         l.onActivityRotation(m.arg1);
     };
 
+    private final TaskStackConsumer mNotifyTaskMovedToBack = (l, m) -> {
+        l.onTaskMovedToBack((RunningTaskInfo) m.obj);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -269,6 +274,9 @@
                 case NOTIFY_ACTIVITY_ROTATED_MSG:
                     forAllRemoteListeners(mNotifyOnActivityRotation, msg);
                     break;
+                case NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskMovedToBack, msg);
+                    break;
             }
             if (msg.obj instanceof SomeArgs) {
                 ((SomeArgs) msg.obj).recycle();
@@ -553,4 +561,13 @@
         forAllLocalListeners(mNotifyOnActivityRotation, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify that a task is being moved behind home.
+     */
+    void notifyTaskMovedToBack(TaskInfo ti) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG, ti);
+        forAllLocalListeners(mNotifyTaskMovedToBack, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4156ed6..ad88469 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2339,7 +2339,7 @@
             if (shouldRelayout) {
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
 
-                result = win.relayoutVisibleWindow(result, attrChanges);
+                result = win.relayoutVisibleWindow(result);
 
                 try {
                     result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
@@ -8411,10 +8411,10 @@
         }
     }
 
-    void grantEmbeddedWindowFocus(Session session, IBinder targetInputToken, boolean grantFocus) {
+    void grantEmbeddedWindowFocus(Session session, IBinder inputToken, boolean grantFocus) {
         synchronized (mGlobalLock) {
             final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
-                    mEmbeddedWindowController.get(targetInputToken);
+                    mEmbeddedWindowController.get(inputToken);
             if (embeddedWindow == null) {
                 Slog.e(TAG, "Embedded window not found");
                 return;
@@ -8426,7 +8426,7 @@
             SurfaceControl.Transaction t = mTransactionFactory.get();
             final int displayId = embeddedWindow.mDisplayId;
             if (grantFocus) {
-                t.setFocusedWindow(targetInputToken, displayId).apply();
+                t.setFocusedWindow(inputToken, embeddedWindow.getName(), displayId).apply();
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Focus request " + embeddedWindow.getName(),
                         "reason=grantEmbeddedWindowFocus(true)");
@@ -8441,7 +8441,8 @@
                             embeddedWindow.getName());
                     return;
                 }
-                t.requestFocusTransfer(newFocusTarget.mInputChannelToken, targetInputToken,
+                t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
+                        inputToken, embeddedWindow.getName(),
                         displayId).apply();
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + newFocusTarget,
@@ -8477,13 +8478,17 @@
             }
             SurfaceControl.Transaction t = mTransactionFactory.get();
             if (grantFocus) {
-                t.requestFocusTransfer(targetInputToken, hostWindow.mInputChannel.getToken(),
+                t.requestFocusTransfer(targetInputToken, embeddedWindow.getName(),
+                        hostWindow.mInputChannel.getToken(),
+                        hostWindow.getName(),
                         hostWindow.getDisplayId()).apply();
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + embeddedWindow.getName(),
                         "reason=grantEmbeddedWindowFocus(true)");
             } else {
-                t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), targetInputToken,
+                t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(),
+                        targetInputToken,
+                        embeddedWindow.getName(),
                         hostWindow.getDisplayId()).apply();
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + hostWindow,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1509ff6..dcab478 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -20,6 +20,7 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
@@ -279,6 +280,9 @@
                     case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
                         effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
                         break;
+                    case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+                        effects |= setAdjacentRootsHierarchyOp(hop);
+                        break;
                     case HIERARCHY_OP_TYPE_REORDER:
                     case HIERARCHY_OP_TYPE_REPARENT:
                         final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
@@ -597,6 +601,17 @@
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
 
+    private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
+        final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask();
+        final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask();
+        if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
+            throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+                    + " organizer root1=" + root1 + " root2=" + root2);
+        }
+        root1.setAdjacentTask(root2);
+        return TRANSACT_EFFECTS_LIFECYCLE;
+    }
+
     private void sanitizeWindowContainer(WindowContainer wc) {
         if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
             throw new RuntimeException("Invalid token in task or displayArea transaction");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b7602fe..5319592 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -55,7 +55,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
@@ -4935,7 +4934,7 @@
         return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
     }
 
-    int relayoutVisibleWindow(int result, int attrChanges) {
+    int relayoutVisibleWindow(int result) {
         final boolean wasVisible = isVisible();
 
         result |= (!wasVisible || !isDrawn()) ? RELAYOUT_RES_FIRST_TIME : 0;
@@ -4970,18 +4969,6 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
-        if ((attrChanges & FORMAT_CHANGED) != 0) {
-            // If the format can't be changed in place, preserve the old surface until the app draws
-            // on the new one. This prevents blinking when we change elevation of freeform and
-            // pinned windows.
-            if (!mWinAnimator.tryChangeFormatInPlaceLocked()) {
-                mWinAnimator.preserveSurfaceLocked(getSyncTransaction());
-                result |= RELAYOUT_RES_SURFACE_CHANGED
-                        | RELAYOUT_RES_FIRST_TIME;
-                scheduleAnimation();
-            }
-        }
-
         // When we change the Surface size, in scenarios which may require changing
         // the surface position in sync with the resize, we use a preserved surface
         // so we can freeze it while waiting for the client to report draw on the newly
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7df9016..8a9ec08 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -378,6 +378,8 @@
     private static final String CREDENTIAL_MANAGEMENT_APP = "credentialManagementApp";
     private static final String NOT_CREDENTIAL_MANAGEMENT_APP = "notCredentialManagementApp";
 
+    private static final String NULL_STRING_ARRAY = "nullStringArray";
+
     // Comprehensive list of delegations.
     private static final String DELEGATIONS[] = {
         DELEGATION_CERT_INSTALL,
@@ -4187,21 +4189,36 @@
      * Calculates strictest (maximum) value for a given password property enforced by admin[s].
      */
     @Override
-    public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle) {
+    public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle,
+            boolean deviceWideOnly) {
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        return getPasswordMinimumMetricsUnchecked(userHandle);
+        return getPasswordMinimumMetricsUnchecked(userHandle, deviceWideOnly);
     }
 
     private PasswordMetrics getPasswordMinimumMetricsUnchecked(@UserIdInt int userId) {
+        return getPasswordMinimumMetricsUnchecked(userId, false);
+    }
+
+    private PasswordMetrics getPasswordMinimumMetricsUnchecked(@UserIdInt int userId,
+            boolean deviceWideOnly) {
         if (!mHasFeature) {
             new PasswordMetrics(CREDENTIAL_TYPE_NONE);
         }
         Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+        if (deviceWideOnly) {
+            Preconditions.checkArgument(!isManagedProfile(userId));
+        }
 
         ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>();
         synchronized (getLockObject()) {
-            List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId);
+            final List<ActiveAdmin> admins;
+            if (deviceWideOnly) {
+                admins = getActiveAdminsForUserAndItsManagedProfilesLocked(userId,
+                        /* shouldIncludeProfileAdmins */ (user) -> false);
+            } else {
+                admins = getActiveAdminsForLockscreenPoliciesLocked(userId);
+            }
             for (ActiveAdmin admin : admins) {
                 final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier();
                 // Use the password metrics from the admin in one of three cases:
@@ -4260,24 +4277,23 @@
         final int parentUser = getProfileParentId(profileUserId);
         enforceUserUnlocked(parentUser);
 
+        final boolean isSufficient;
         synchronized (getLockObject()) {
 
-            // Combine password policies across the user and its profiles. Profile admins are
-            // excluded since we only want explicit password requirements, while profile admin
-            // requirement are applicable only when the profile has unified challenge.
-            List<ActiveAdmin> admins = getActiveAdminsForUserAndItsManagedProfilesLocked(parentUser,
-                    /* shouldIncludeProfileAdmins */ (user) -> false);
-            ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size());
-            int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
-            for (ActiveAdmin admin : admins) {
-                adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
-                maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
-            }
+            int complexity = getAggregatedPasswordComplexityLocked(parentUser, true);
+            PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(parentUser, true);
 
             PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(parentUser);
-            return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
-                    maxRequiredComplexity, false, metrics).isEmpty();
+            final List<PasswordValidationError> passwordValidationErrors =
+                    PasswordMetrics.validatePasswordMetrics(
+                            minMetrics, complexity, false, metrics);
+            isSufficient = passwordValidationErrors.isEmpty();
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.IS_ACTIVE_PASSWORD_SUFFICIENT_FOR_DEVICE)
+                .setStrings(mOwners.getProfileOwnerComponent(caller.getUserId()).getPackageName())
+                .write();
+        return isSufficient;
     }
 
     @Override
@@ -4380,7 +4396,7 @@
      */
     private boolean isPasswordSufficientForUserWithoutCheckpointLocked(
             @NonNull PasswordMetrics metrics, @UserIdInt int userId) {
-        final int complexity = getEffectivePasswordComplexityRequirementLocked(userId);
+        final int complexity = getAggregatedPasswordComplexityLocked(userId);
         PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userId);
         final List<PasswordValidationError> passwordValidationErrors =
                 PasswordMetrics.validatePasswordMetrics(
@@ -4482,9 +4498,20 @@
         }
     }
 
-    private int getEffectivePasswordComplexityRequirementLocked(@UserIdInt int userHandle) {
+    private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle) {
+        return getAggregatedPasswordComplexityLocked(userHandle, false);
+    }
+
+    private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle,
+            boolean deviceWideOnly) {
         ensureLocked();
-        List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
+        final List<ActiveAdmin> admins;
+        if (deviceWideOnly) {
+            admins = getActiveAdminsForUserAndItsManagedProfilesLocked(userHandle,
+                    /* shouldIncludeProfileAdmins */ (user) -> false);
+        } else {
+            admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
+        }
         int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
         for (ActiveAdmin admin : admins) {
             maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
@@ -4512,7 +4539,7 @@
     }
 
     @Override
-    public int getAggregatedPasswordComplexityForUser(int userId) {
+    public int getAggregatedPasswordComplexityForUser(int userId, boolean deviceWideOnly) {
         if (!mHasFeature) {
             return PASSWORD_COMPLEXITY_NONE;
         }
@@ -4521,7 +4548,7 @@
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
 
         synchronized (getLockObject()) {
-            return getEffectivePasswordComplexityRequirementLocked(userId);
+            return getAggregatedPasswordComplexityLocked(userId, deviceWideOnly);
         }
     }
 
@@ -4716,7 +4743,7 @@
         synchronized (getLockObject()) {
             final PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userHandle);
             final List<PasswordValidationError> validationErrors;
-            final int complexity = getEffectivePasswordComplexityRequirementLocked(userHandle);
+            final int complexity = getAggregatedPasswordComplexityLocked(userHandle);
             // TODO: Consider changing validation API to take LockscreenCredential.
             if (password.isEmpty()) {
                 validationErrors = PasswordMetrics.validatePasswordMetrics(
@@ -9559,59 +9586,86 @@
     }
 
     @Override
-    public boolean setPermittedInputMethods(ComponentName who, List packageList) {
+    public boolean setPermittedInputMethods(ComponentName who, List packageList,
+            boolean calledOnParentInstance) {
         if (!mHasFeature) {
             return false;
         }
         Objects.requireNonNull(who, "ComponentName is null");
 
         final CallerIdentity caller = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+        final int userId = getProfileParentUserIfRequested(
+                caller.getUserId(), calledOnParentInstance);
+        if (calledOnParentInstance) {
+            Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+            Preconditions.checkArgument(packageList == null || packageList.isEmpty(),
+                    "Permitted input methods must allow all input methods or only "
+                            + "system input methods when called on the parent instance of an "
+                            + "organization-owned device");
+        } else {
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+        }
 
         if (packageList != null) {
-            List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
-                    .getEnabledInputMethodListAsUser(caller.getUserId());
+            List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
+                    InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
             if (enabledImes != null) {
                 List<String> enabledPackages = new ArrayList<String>();
                 for (InputMethodInfo ime : enabledImes) {
                     enabledPackages.add(ime.getPackageName());
                 }
                 if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
-                        caller.getUserId())) {
-                    Slog.e(LOG_TAG, "Cannot set permitted input methods, "
-                            + "because it contains already enabled input method.");
+                        userId)) {
+                    Slog.e(LOG_TAG, "Cannot set permitted input methods, because the list of "
+                            + "permitted input methods excludes an already-enabled input method.");
                     return false;
                 }
             }
         }
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            final ActiveAdmin admin = getParentOfAdminIfRequired(
+                    getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParentInstance);
             admin.permittedInputMethods = packageList;
             saveSettingsLocked(caller.getUserId());
         }
-        final String[] packageArray =
-                packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
+
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PERMITTED_INPUT_METHODS)
                 .setAdmin(who)
-                .setStrings(packageArray)
+                .setStrings(getStringArrayForLogging(packageList, calledOnParentInstance))
                 .write();
         return true;
     }
 
+    private String[] getStringArrayForLogging(List list, boolean calledOnParentInstance) {
+        List<String> stringList = new ArrayList<String>();
+        stringList.add(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT);
+        if (list == null) {
+            stringList.add(NULL_STRING_ARRAY);
+        } else {
+            stringList.addAll((List<String>) list);
+        }
+        return stringList.toArray(new String[0]);
+    }
+
     @Override
-    public List getPermittedInputMethods(ComponentName who) {
+    public List getPermittedInputMethods(ComponentName who, boolean calledOnParentInstance) {
         if (!mHasFeature) {
             return null;
         }
         Objects.requireNonNull(who, "ComponentName is null");
 
         final CallerIdentity caller = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+        if (calledOnParentInstance) {
+            Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+        } else {
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+        }
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            final ActiveAdmin admin = getParentOfAdminIfRequired(
+                    getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParentInstance);
             return admin.permittedInputMethods;
         }
     }
@@ -9624,9 +9678,8 @@
         synchronized (getLockObject()) {
             List<String> result = null;
             // Only device or profile owners can have permitted lists set.
-            DevicePolicyData policy = getUserDataUnchecked(caller.getUserId());
-            for (int i = 0; i < policy.mAdminList.size(); i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
+            List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(caller.getUserId());
+            for (ActiveAdmin admin: admins) {
                 List<String> fromAdmin = admin.permittedInputMethods;
                 if (fromAdmin != null) {
                     if (result == null) {
@@ -9657,7 +9710,7 @@
 
     @Override
     public boolean isInputMethodPermittedByAdmin(ComponentName who, String packageName,
-            int userHandle) {
+            int userHandle, boolean calledOnParentInstance) {
         if (!mHasFeature) {
             return true;
         }
@@ -9666,7 +9719,8 @@
         enforceSystemCaller("query if an input method is disabled by admin");
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+            ActiveAdmin admin = getParentOfAdminIfRequired(
+                    getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance);
             if (admin == null) {
                 return false;
             }
@@ -15868,8 +15922,9 @@
         Log.i(LOG_TAG,
                 String.format("Setting Enterprise ID to %s for user %d", organizationId, userId));
 
+        final String ownerPackage;
         synchronized (getLockObject()) {
-            ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+            final ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
             // As the caller is the system, it must specify the component name of the profile owner
             // as a safety check.
             Preconditions.checkCallAuthorization(
@@ -15877,6 +15932,7 @@
                     String.format("The Profile Owner or Device Owner may only set the Enterprise ID"
                             + " on its own user, called on user %d but owner user is %d", userId,
                             owner.getUserHandle().getIdentifier()));
+            ownerPackage = owner.info.getPackageName();
             Preconditions.checkState(
                     TextUtils.isEmpty(owner.mOrganizationId) || owner.mOrganizationId.equals(
                             organizationId),
@@ -15894,5 +15950,11 @@
                 saveSettingsLocked(userId);
             });
         }
+
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_ORGANIZATION_ID)
+                .setAdmin(ownerPackage)
+                .setBoolean(isManagedProfile(userId))
+                .write();
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 57b3e8d..c613dfb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -323,6 +323,8 @@
             "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
     private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS =
             "com.android.server.location.timezone.LocationTimeZoneManagerService$Lifecycle";
+    private static final String GNSS_TIME_UPDATE_SERVICE_CLASS =
+            "com.android.server.timedetector.GnssTimeUpdateService$Lifecycle";
     private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
             "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
     private static final String ADB_SERVICE_CLASS =
@@ -1874,6 +1876,16 @@
             }
             t.traceEnd();
 
+            if (context.getResources().getBoolean(R.bool.config_enableGnssTimeUpdateService)) {
+                t.traceBegin("StartGnssTimeUpdateService");
+                try {
+                    mSystemServiceManager.startService(GNSS_TIME_UPDATE_SERVICE_CLASS);
+                } catch (Throwable e) {
+                    reportWtf("starting GnssTimeUpdateService service", e);
+                }
+                t.traceEnd();
+            }
+
             if (!isWatch) {
                 t.traceBegin("StartSearchManagerService");
                 try {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index d032dbd..8011ec2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1655,7 +1655,7 @@
         sService.mConstants.KEEP_WARMING_SERVICES.clear();
         final ServiceInfo si = mock(ServiceInfo.class);
         si.applicationInfo = mock(ApplicationInfo.class);
-        ServiceRecord s = spy(new ServiceRecord(sService, null, cn, cn, null, 0, null,
+        ServiceRecord s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
                 si, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
         s.startRequested = true;
@@ -1668,7 +1668,7 @@
         final ServiceInfo si2 = mock(ServiceInfo.class);
         si2.applicationInfo = mock(ApplicationInfo.class);
         si2.applicationInfo.uid = MOCKAPP2_UID_OTHER;
-        ServiceRecord s2 = spy(new ServiceRecord(sService, null, cn2, cn2, null, 0, null,
+        ServiceRecord s2 = spy(new ServiceRecord(sService, cn2, cn2, null, 0, null,
                 si2, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections();
         s2.startRequested = true;
@@ -1708,7 +1708,7 @@
         app.hasShownUi = true;
         sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
         sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
-        s = spy(new ServiceRecord(sService, null, cn, cn, null, 0, null,
+        s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
                 si, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
         s.startRequested = true;
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java
index df533f3..adeb44b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java
@@ -58,7 +58,7 @@
 
     @Test
     public void testSingleRateLimit_belowLimit_isWithinQuota() {
-        MultiRateLimiter multiRateLimiter =  new MultiRateLimiter.Builder(mContext, mInjector)
+        MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
                 .addRateLimit(3, Duration.ofSeconds(20))
                 .build();
 
@@ -77,7 +77,7 @@
 
     @Test
     public void testSingleRateLimit_aboveLimit_isNotWithinQuota() {
-        MultiRateLimiter multiRateLimiter =  new MultiRateLimiter.Builder(mContext, mInjector)
+        MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
                 .addRateLimit(3, Duration.ofSeconds(20))
                 .build();
 
@@ -97,7 +97,7 @@
 
     @Test
     public void testSingleRateLimit_afterGoingAboveQuotaAndWaitingWindow_isBackWithinQuota() {
-        MultiRateLimiter multiRateLimiter =  new MultiRateLimiter.Builder(mContext, mInjector)
+        MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
                 .addRateLimit(3, Duration.ofSeconds(20))
                 .build();
 
@@ -166,7 +166,7 @@
 
     @Test
     public void createSingleRateLimit_testItLimitsOnlyGivenUptc() {
-        MultiRateLimiter multiRateLimiter =  new MultiRateLimiter.Builder(mContext, mInjector)
+        MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
                 .addRateLimit(3, Duration.ofSeconds(20))
                 .build();
 
@@ -194,4 +194,48 @@
         assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
         assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
     }
+
+    @Test
+    public void clearRateLimiterForPackage_afterReachingQuota_quotaIsReset() {
+        MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+                .addRateLimit(1, Duration.ofSeconds(100))
+                .build();
+
+        mInjector.mElapsedTime = Duration.ZERO;
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+        multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+        mInjector.mElapsedTime = Duration.ofSeconds(1);
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+
+        multiRateLimiter.clear(USER_ID, PACKAGE_NAME_1);
+
+        // Quota for that package is reset.
+        mInjector.mElapsedTime = Duration.ofSeconds(1);
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+
+        // Quota is enforced again.
+        mInjector.mElapsedTime = Duration.ofSeconds(1);
+        multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+    }
+
+    @Test
+    public void clearRateLimiterForPackage_doesntAffectOtherPackages() {
+        MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+                .addRateLimit(1, Duration.ofSeconds(100))
+                .build();
+
+        mInjector.mElapsedTime = Duration.ZERO;
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
+        multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_2, TAG);
+
+        mInjector.mElapsedTime = Duration.ofSeconds(1);
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isFalse();
+
+        multiRateLimiter.clear(USER_ID, PACKAGE_NAME_1);
+
+        // Doesn't affect the other package.
+        assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index b360ae8..e119d52 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -21,12 +21,12 @@
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
-import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
 import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.util.DebugUtils.valueToString;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -69,7 +69,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
@@ -105,7 +104,6 @@
  *  atest FrameworksServicesTests:ActivityManagerServiceTest
  */
 @SmallTest
-@FlakyTest(bugId = 113616538)
 public class ActivityManagerServiceTest {
     private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
 
@@ -199,8 +197,8 @@
 
         // Uid state is not moving from background to foreground or vice versa.
         verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_IMPORTANT_BACKGROUND, // prevState
-                PROCESS_STATE_IMPORTANT_FOREGROUND, // curState
+                PROCESS_STATE_TRANSIENT_BACKGROUND, // prevState
+                PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
                 42, // expectedGlobalCounter
                 1, // exptectedCurProcStateSeq
                 NETWORK_STATE_NO_CHANGE, // expectedBlockState
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 6366155..b2a860d 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
@@ -27,6 +27,11 @@
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
 import com.android.server.appsearch.proto.DocumentProto;
@@ -55,11 +60,30 @@
 
 public class AppSearchImplTest {
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private MockPackageManager mMockPackageManager = new MockPackageManager();
+    private Context mContext;
     private AppSearchImpl mAppSearchImpl;
+    private int mGlobalQuerierUid;
 
     @Before
     public void setUp() throws Exception {
-        mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
+        Context context = ApplicationProvider.getApplicationContext();
+        mContext =
+                new ContextWrapper(context) {
+                    @Override
+                    public PackageManager getPackageManager() {
+                        return mMockPackageManager.getMockPackageManager();
+                    }
+                };
+
+        // Give ourselves global query permissions
+        mAppSearchImpl =
+                AppSearchImpl.create(
+                        mTemporaryFolder.newFolder(),
+                        mContext,
+                        /*globalQuerierPackage=*/ mContext.getPackageName());
+        mGlobalQuerierUid =
+                mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
     }
 
     // TODO(b/175430168) add test to verify reset is working properly.
@@ -460,7 +484,8 @@
         // Rewrite SearchSpec
         mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
                 searchSpecProto,
-                Collections.singleton(AppSearchImpl.createPrefix("package", "database")));
+                Collections.singleton(AppSearchImpl.createPrefix("package", "database")),
+                ImmutableSet.of("package$database/type"));
         assertThat(searchSpecProto.getSchemaTypeFiltersList())
                 .containsExactly("package$database/type");
         assertThat(searchSpecProto.getNamespaceFiltersList())
@@ -505,7 +530,10 @@
                 searchSpecProto,
                 ImmutableSet.of(
                         AppSearchImpl.createPrefix("package", "database1"),
-                        AppSearchImpl.createPrefix("package", "database2")));
+                        AppSearchImpl.createPrefix("package", "database2")),
+                ImmutableSet.of(
+                        "package$database1/typeA", "package$database1/typeB",
+                        "package$database2/typeA", "package$database2/typeB"));
         assertThat(searchSpecProto.getSchemaTypeFiltersList())
                 .containsExactly(
                         "package$database1/typeA",
@@ -517,6 +545,38 @@
     }
 
     @Test
+    public void testRewriteSearchSpec_ignoresSearchSpecSchemaFilters() throws Exception {
+        SearchSpecProto.Builder searchSpecProto =
+                SearchSpecProto.newBuilder().setQuery("").addSchemaTypeFilters("type");
+
+        // Insert schema
+        List<AppSearchSchema> schemas =
+                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                schemas,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+
+        // Insert document
+        GenericDocument document =
+                new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
+        mAppSearchImpl.putDocument("package", "database", document);
+
+        // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to
+        // search over. Despite the searchSpecProto having schema type filters.
+        assertThat(
+                        mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
+                                searchSpecProto,
+                                Collections.singleton(
+                                        AppSearchImpl.createPrefix("package", "database")),
+                                /*allowedPrefixedSchemas=*/ Collections.emptySet()))
+                .isFalse();
+    }
+
+    @Test
     public void testQueryEmptyDatabase() throws Exception {
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
@@ -640,7 +700,9 @@
     public void testGlobalQueryEmptyDatabase() throws Exception {
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), /*callerUid=*/ 0);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
@@ -685,7 +747,9 @@
         // No query filters specified, global query can retrieve all documents.
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
         assertThat(searchResultPage.getResults()).hasSize(2);
 
         // Document2 will be first since it got indexed later and has a "better", aka more recent
@@ -738,7 +802,9 @@
                         .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
                         .addFilterPackageNames("package1")
                         .build();
-        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
 
@@ -748,7 +814,9 @@
                         .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
                         .addFilterPackageNames("package2")
                         .build();
-        searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
+        searchResultPage =
+                mAppSearchImpl.globalQuery(
+                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
     }
@@ -794,7 +862,8 @@
                 SchemaProto.newBuilder()
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Email"))
+                                        .setSchemaType("package$database1/Email")
+                                        .setVersion(0))
                         .build();
 
         List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
@@ -806,9 +875,16 @@
 
     @Test
     public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
-        PackageIdentifier package1 =
-                new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[] {10};
+        int uidFoo = 1;
 
+        // Make sure foo package will pass package manager checks.
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+
+        // Set schema1
         String prefix = AppSearchImpl.createPrefix("package", "database");
         mAppSearchImpl.setSchema(
                 "package",
@@ -816,19 +892,21 @@
                 Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
                 /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
                 /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1", ImmutableList.of(package1)),
+                        "schema1",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false);
 
         // "schema1" is platform hidden now and package visible to package1
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
                 .isFalse();
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
                 .isTrue();
 
         // Add a new schema, and include the already-existing "schema1"
@@ -840,31 +918,34 @@
                         new AppSearchSchema.Builder("schema2").build()),
                 /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
                 /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1", ImmutableList.of(package1)),
+                        "schema1",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false);
 
         // Check that "schema1" still has the same visibility settings
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
                 .isFalse();
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
                 .isTrue();
 
         // "schema2" has default visibility settings
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "schema2"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema2", mGlobalQuerierUid))
                 .isTrue();
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "schema2", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema2", uidFoo))
                 .isFalse();
     }
 
@@ -891,10 +972,12 @@
                 SchemaProto.newBuilder()
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Email"))
+                                        .setSchemaType("package$database1/Email")
+                                        .setVersion(0))
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Document"))
+                                        .setSchemaType("package$database1/Document")
+                                        .setVersion(0))
                         .build();
 
         // Check both schema Email and Document saved correctly.
@@ -935,7 +1018,8 @@
                 SchemaProto.newBuilder()
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Email"))
+                                        .setSchemaType("package$database1/Email")
+                                        .setVersion(0))
                         .build();
 
         expectedTypes = new ArrayList<>();
@@ -977,16 +1061,20 @@
                 SchemaProto.newBuilder()
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Email"))
+                                        .setSchemaType("package$database1/Email")
+                                        .setVersion(0))
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Document"))
+                                        .setSchemaType("package$database1/Document")
+                                        .setVersion(0))
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database2/Email"))
+                                        .setSchemaType("package$database2/Email")
+                                        .setVersion(0))
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database2/Document"))
+                                        .setSchemaType("package$database2/Document")
+                                        .setVersion(0))
                         .build();
 
         // Check Email and Document is saved in database 1 and 2 correctly.
@@ -1012,13 +1100,16 @@
                 SchemaProto.newBuilder()
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database1/Email"))
+                                        .setSchemaType("package$database1/Email")
+                                        .setVersion(0))
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database2/Email"))
+                                        .setSchemaType("package$database2/Email")
+                                        .setVersion(0))
                         .addTypes(
                                 SchemaTypeConfigProto.newBuilder()
-                                        .setSchemaType("package$database2/Document"))
+                                        .setSchemaType("package$database2/Document")
+                                        .setVersion(0))
                         .build();
 
         // Check nothing changed in database2.
@@ -1031,8 +1122,14 @@
 
     @Test
     public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
-        PackageIdentifier package1 =
-                new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[] {10};
+        int uidFoo = 1;
+
+        // Make sure foo package will pass package manager checks.
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
 
         String prefix = AppSearchImpl.createPrefix("package", "database");
         mAppSearchImpl.setSchema(
@@ -1041,19 +1138,21 @@
                 Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
                 /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
                 /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1", ImmutableList.of(package1)),
+                        "schema1",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false);
 
         // "schema1" is platform hidden now and package accessible
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
                 .isFalse();
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
                 .isTrue();
 
         // Remove "schema1" by force overriding
@@ -1069,12 +1168,13 @@
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
                 .isTrue();
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
                 .isFalse();
 
         // Add "schema1" back, it gets default visibility settings which means it's not platform
@@ -1089,12 +1189,13 @@
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "schema1", mGlobalQuerierUid))
                 .isTrue();
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "schema1", uidFoo))
                 .isFalse();
     }
 
@@ -1111,7 +1212,8 @@
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "Schema"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "Schema", mGlobalQuerierUid))
                 .isTrue();
     }
 
@@ -1128,15 +1230,13 @@
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPlatformSurfaceable(prefix, prefix + "Schema"))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "Schema", mGlobalQuerierUid))
                 .isFalse();
     }
 
     @Test
     public void testSetSchema_defaultNotPackageAccessible() throws Exception {
-        PackageIdentifier package1 =
-                new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
-
         String prefix = AppSearchImpl.createPrefix("package", "database");
         mAppSearchImpl.setSchema(
                 "package",
@@ -1148,14 +1248,21 @@
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "Schema", package1))
+                                .isSchemaSearchableByCaller(
+                                        prefix, prefix + "Schema", /*callerUid=*/ 42))
                 .isFalse();
     }
 
     @Test
     public void testSetSchema_packageAccessible() throws Exception {
-        PackageIdentifier package1 =
-                new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[] {10};
+        int uidFoo = 1;
+
+        // Make sure foo package will pass package manager checks.
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
 
         String prefix = AppSearchImpl.createPrefix("package", "database");
         mAppSearchImpl.setSchema(
@@ -1163,12 +1270,14 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of("Schema", ImmutableList.of(package1)),
+                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                        "Schema",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false);
         assertThat(
                         mAppSearchImpl
                                 .getVisibilityStoreLocked()
-                                .isSchemaPackageAccessible(prefix, prefix + "Schema", package1))
+                                .isSchemaSearchableByCaller(prefix, prefix + "Schema", uidFoo))
                 .isTrue();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/MockPackageManager.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/MockPackageManager.java
new file mode 100644
index 0000000..c2a8243
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/MockPackageManager.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Mock to help test package name, UID, and certificate verification
+ *
+ * @hide
+ */
+public class MockPackageManager {
+
+    @Mock private PackageManager mMockPackageManager;
+
+    public MockPackageManager() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @NonNull
+    public PackageManager getMockPackageManager() {
+        return mMockPackageManager;
+    }
+
+    /** Mock a NameNotFoundException if the package name isn't installed. */
+    public void mockThrowsNameNotFoundException(String packageName) {
+        try {
+            when(mMockPackageManager.getPackageUid(eq(packageName), /*flags=*/ anyInt()))
+                    .thenThrow(new PackageManager.NameNotFoundException());
+        } catch (PackageManager.NameNotFoundException e) {
+            // Shouldn't ever happen since we're mocking the exception
+            e.printStackTrace();
+        }
+    }
+
+    /** Mocks that {@code uid} contains the {@code packageName} */
+    public void mockGetPackageUid(String packageName, int uid) {
+        try {
+            when(mMockPackageManager.getPackageUid(eq(packageName), /*flags=*/ anyInt()))
+                    .thenReturn(uid);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Shouldn't ever happen since we're mocking the method.
+            e.printStackTrace();
+        }
+    }
+
+    /** Mocks that {@code packageName} has been signed with {@code sha256Cert}. */
+    public void mockAddSigningCertificate(String packageName, byte[] sha256Cert) {
+        when(mMockPackageManager.hasSigningCertificate(
+                        packageName, sha256Cert, PackageManager.CERT_INPUT_SHA256))
+                .thenReturn(true);
+    }
+
+    /** Mocks that {@code packageName} has NOT been signed with {@code sha256Cert}. */
+    public void mockRemoveSigningCertificate(String packageName, byte[] sha256Cert) {
+        when(mMockPackageManager.hasSigningCertificate(
+                        packageName, sha256Cert, PackageManager.CERT_INPUT_SHA256))
+                .thenReturn(false);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
index e491ac3..da3e999 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
@@ -19,6 +19,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.appsearch.PackageIdentifier;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -34,12 +39,32 @@
 public class VisibilityStoreTest {
 
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private MockPackageManager mMockPackageManager = new MockPackageManager();
+    private Context mContext;
     private AppSearchImpl mAppSearchImpl;
     private VisibilityStore mVisibilityStore;
+    private int mGlobalQuerierUid;
 
     @Before
     public void setUp() throws Exception {
-        mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
+        Context context = ApplicationProvider.getApplicationContext();
+        mContext =
+                new ContextWrapper(context) {
+                    @Override
+                    public PackageManager getPackageManager() {
+                        return mMockPackageManager.getMockPackageManager();
+                    }
+                };
+
+        // Give ourselves global query permissions
+        mAppSearchImpl =
+                AppSearchImpl.create(
+                        mTemporaryFolder.newFolder(),
+                        mContext,
+                        /*globalQuerierPackage=*/ mContext.getPackageName());
+        mGlobalQuerierUid =
+                mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
+
         mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked();
     }
 
@@ -80,9 +105,13 @@
                 /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
                         "prefix/schema1", "prefix/schema2"),
                 /*schemasPackageAccessible=*/ Collections.emptyMap());
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema1", mGlobalQuerierUid))
                 .isFalse();
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema2", mGlobalQuerierUid))
                 .isFalse();
 
         // New .setVisibility() call completely overrides previous visibility settings. So
@@ -92,92 +121,198 @@
                 /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
                         "prefix/schema1", "prefix/schema3"),
                 /*schemasPackageAccessible=*/ Collections.emptyMap());
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema1", mGlobalQuerierUid))
                 .isFalse();
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema2", mGlobalQuerierUid))
                 .isTrue();
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema3"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema3", mGlobalQuerierUid))
                 .isFalse();
 
+        // Everything defaults to visible again.
         mVisibilityStore.setVisibility(
                 "prefix",
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
                 /*schemasPackageAccessible=*/ Collections.emptyMap());
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema1", mGlobalQuerierUid))
                 .isTrue();
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema2", mGlobalQuerierUid))
                 .isTrue();
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema3"))
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schema3", mGlobalQuerierUid))
                 .isTrue();
     }
 
     @Test
-    public void testSetVisibility_packageAccessible() throws Exception {
-        PackageIdentifier package1 =
-                new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
-        PackageIdentifier package2 =
-                new PackageIdentifier("package2", /*sha256Certificate=*/ new byte[] {100});
-        PackageIdentifier package3 =
-                new PackageIdentifier("package3", /*sha256Certificate=*/ new byte[] {100});
+    public void testIsSchemaSearchableByCaller_platformQuerierHandlesNameNotFoundException()
+            throws Exception {
+        // Initialized the VisibilityStore with this context's package name as the global querier.
+        mMockPackageManager.mockThrowsNameNotFoundException(mContext.getPackageName());
 
+        // Create a new VisibilityStore instance since we look up the UID on initialization
+        AppSearchImpl appSearchImpl =
+                AppSearchImpl.create(
+                        mTemporaryFolder.newFolder(),
+                        mContext,
+                        /*globalQuerierPackage=*/ mContext.getPackageName());
+        VisibilityStore visibilityStore = appSearchImpl.getVisibilityStoreLocked();
+
+        // Use some arbitrary callerUid. If we can't find the global querier's uid though,
+        // nothing should be platform surfaceable.
+        assertThat(
+                        visibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", /*callerUid=*/ 0))
+                .isFalse();
+    }
+
+    @Test
+    public void testSetVisibility_packageAccessible() throws Exception {
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[] {10};
+        int uidFoo = 1;
+
+        // Values for a "bar" client
+        String packageNameBar = "packageBar";
+        byte[] sha256CertBar = new byte[] {100};
+        int uidBar = 2;
+
+        // Can't be the same value as uidFoo nor uidBar
+        int uidNotFooOrBar = 3;
+
+        // By default, a schema isn't package accessible.
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", uidFoo))
+                .isFalse();
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaBar", uidBar))
+                .isFalse();
+
+        // Grant package access
         mVisibilityStore.setVisibility(
                 "prefix",
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
                 /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "prefix/schema1", ImmutableList.of(package1),
-                        "prefix/schema2", ImmutableList.of(package2)));
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
+                        "prefix/schemaFoo",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)),
+                        "prefix/schemaBar",
+                        ImmutableList.of(new PackageIdentifier(packageNameBar, sha256CertBar))));
+
+        // Should fail if PackageManager doesn't see that it has the proper certificate
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockRemoveSigningCertificate(packageNameFoo, sha256CertFoo);
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", uidFoo))
+                .isFalse();
+
+        // Should fail if PackageManager doesn't think the package belongs to the uid
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidNotFooOrBar);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", uidFoo))
+                .isFalse();
+
+        // But if uid and certificate match, then we should have access
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", uidFoo))
                 .isTrue();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
+
+        mMockPackageManager.mockGetPackageUid(packageNameBar, uidBar);
+        mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaBar", uidBar))
                 .isTrue();
 
         // New .setVisibility() call completely overrides previous visibility settings. So
-        // "schema2" isn't preserved.
+        // "schemaBar" settings aren't preserved.
         mVisibilityStore.setVisibility(
                 "prefix",
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
                 /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "prefix/schema1", ImmutableList.of(package1),
-                        "prefix/schema3", ImmutableList.of(package3)));
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
-                .isTrue();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
-                .isFalse();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema3", package3))
+                        "prefix/schemaFoo",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
+
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", uidFoo))
                 .isTrue();
 
+        mMockPackageManager.mockGetPackageUid(packageNameBar, uidBar);
+        mMockPackageManager.mockAddSigningCertificate(packageNameBar, sha256CertBar);
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaBar", uidBar))
+                .isFalse();
+    }
+
+    @Test
+    public void testIsSchemaSearchableByCaller_packageAccessibilityHandlesNameNotFoundException()
+            throws Exception {
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[] {10};
+        int uidFoo = 1;
+
+        // Pretend we can't find the Foo package.
+        mMockPackageManager.mockThrowsNameNotFoundException(packageNameFoo);
+
+        // Grant package access
         mVisibilityStore.setVisibility(
                 "prefix",
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap());
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
-                .isFalse();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
-                .isFalse();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema3", package3))
+                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                        "prefix/schemaFoo",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
+
+        // If we can't verify the Foo package that has access, assume it doesn't have access.
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                "prefix", "prefix/schemaFoo", uidFoo))
                 .isFalse();
     }
 
     @Test
     public void testEmptyPrefix() throws Exception {
-        PackageIdentifier package1 =
-                new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
-        PackageIdentifier package2 =
-                new PackageIdentifier("package2", /*sha256Certificate=*/ new byte[] {100});
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[] {10};
+        int uidFoo = 1;
 
         mVisibilityStore.setVisibility(
                 /*prefix=*/ "",
-                /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
                 /*schemasPackageAccessible=*/ ImmutableMap.of(
-                        "schema1", ImmutableList.of(package1),
-                        "schema2", ImmutableList.of(package2)));
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema1"))
-                .isFalse();
-        assertThat(mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema2"))
-                .isFalse();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible(/*prefix=*/ "", "schema1", package1))
+                        "schema",
+                        ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
+
+        assertThat(
+                        mVisibilityStore.isSchemaSearchableByCaller(
+                                /*prefix=*/ "", "schema", mGlobalQuerierUid))
                 .isTrue();
-        assertThat(mVisibilityStore.isSchemaPackageAccessible(/*prefix=*/ "", "schema2", package2))
+
+        mMockPackageManager.mockGetPackageUid(packageNameFoo, uidFoo);
+        mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
+        assertThat(mVisibilityStore.isSchemaSearchableByCaller(/*prefix=*/ "", "schema", uidFoo))
                 .isTrue();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
index 88edcb8..dc225f1 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
@@ -32,35 +32,35 @@
     public void testGetProto_Email() {
         AppSearchSchema emailSchema =
                 new AppSearchSchema.Builder("Email")
+                        .setVersion(12345)
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("subject")
-                                        .setDataType(
-                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .setIndexingType(
-                                                AppSearchSchema.PropertyConfig
+                                                AppSearchSchema.StringPropertyConfig
                                                         .INDEXING_TYPE_PREFIXES)
                                         .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
                                         .build())
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("body")
-                                        .setDataType(
-                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                new AppSearchSchema.StringPropertyConfig.Builder("body")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .setIndexingType(
-                                                AppSearchSchema.PropertyConfig
+                                                AppSearchSchema.StringPropertyConfig
                                                         .INDEXING_TYPE_PREFIXES)
                                         .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
                                         .build())
                         .build();
 
         SchemaTypeConfigProto expectedEmailProto =
                 SchemaTypeConfigProto.newBuilder()
                         .setSchemaType("Email")
+                        .setVersion(12345)
                         .addProperties(
                                 PropertyConfigProto.newBuilder()
                                         .setPropertyName("subject")
@@ -100,32 +100,27 @@
         AppSearchSchema musicRecordingSchema =
                 new AppSearchSchema.Builder("MusicRecording")
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("artist")
-                                        .setDataType(
-                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                new AppSearchSchema.StringPropertyConfig.Builder("artist")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
                                         .setIndexingType(
-                                                AppSearchSchema.PropertyConfig
+                                                AppSearchSchema.StringPropertyConfig
                                                         .INDEXING_TYPE_PREFIXES)
                                         .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
                                         .build())
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("pubDate")
-                                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                                new AppSearchSchema.Int64PropertyConfig.Builder("pubDate")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setIndexingType(
-                                                AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
-                                        .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
                                         .build())
                         .build();
 
         SchemaTypeConfigProto expectedMusicRecordingProto =
                 SchemaTypeConfigProto.newBuilder()
                         .setSchemaType("MusicRecording")
+                        .setVersion(0)
                         .addProperties(
                                 PropertyConfigProto.newBuilder()
                                         .setPropertyName("artist")
@@ -144,14 +139,7 @@
                                         .setPropertyName("pubDate")
                                         .setDataType(PropertyConfigProto.DataType.Code.INT64)
                                         .setCardinality(
-                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                        .setStringIndexingConfig(
-                                                StringIndexingConfig.newBuilder()
-                                                        .setTokenizerType(
-                                                                StringIndexingConfig.TokenizerType
-                                                                        .Code.NONE)
-                                                        .setTermMatchType(
-                                                                TermMatchType.Code.UNKNOWN)))
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL))
                         .build();
 
         assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(musicRecordingSchema))
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 a455ba9..065a2f3 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 com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -2333,6 +2334,32 @@
     }
 
     @Test
+    public void testSetPermittedInputMethodsWithPOOfOrganizationOwnedDevice()
+            throws Exception {
+        String packageName = "com.google.pkg.one";
+        setupProfileOwner();
+        configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+        // Allow all input methods
+        parentDpm.setPermittedInputMethods(admin1, null);
+
+        assertThat(parentDpm.getPermittedInputMethods(admin1)).isNull();
+
+        // Allow only system input methods
+        parentDpm.setPermittedInputMethods(admin1, new ArrayList<>());
+
+        assertThat(parentDpm.getPermittedInputMethods(admin1)).isEmpty();
+
+        // Don't allow specific third party input methods
+        final List<String> inputMethods = Collections.singletonList(packageName);
+
+        assertExpectException(IllegalArgumentException.class, /* messageRegex= */ "Permitted "
+                        + "input methods must allow all input methods or only system input methods "
+                        + "when called on the parent instance of an organization-owned device",
+                () -> parentDpm.setPermittedInputMethods(admin1, inputMethods));
+    }
+
+    @Test
     public void testSetKeyguardDisabledFeaturesWithDO() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
@@ -5133,6 +5160,54 @@
     }
 
     @Test
+    public void testGetAggregatedPasswordComplexity_IgnoreProfileRequirement()
+            throws Exception {
+        final int managedProfileUserId = CALLER_USER_HANDLE;
+        final int managedProfileAdminUid =
+                UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+
+        dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
+        parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
+
+        assertThat(dpms.getAggregatedPasswordComplexityForUser(UserHandle.USER_SYSTEM, true))
+                .isEqualTo(PASSWORD_COMPLEXITY_LOW);
+        assertThat(dpms.getAggregatedPasswordComplexityForUser(UserHandle.USER_SYSTEM, false))
+                .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+    }
+
+    @Test
+    public void testGetAggregatedPasswordMetrics_IgnoreProfileRequirement()
+            throws Exception {
+        final int managedProfileUserId = CALLER_USER_HANDLE;
+        final int managedProfileAdminUid =
+                UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+
+        dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+        dpm.setPasswordMinimumLength(admin1, 8);
+        dpm.setPasswordMinimumLetters(admin1, 1);
+        dpm.setPasswordMinimumNumeric(admin1, 2);
+        dpm.setPasswordMinimumSymbols(admin1, 3);
+
+        parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+
+        PasswordMetrics deviceMetrics =
+                dpms.getPasswordMinimumMetrics(UserHandle.USER_SYSTEM, true);
+        assertThat(deviceMetrics.credType).isEqualTo(LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
+
+        PasswordMetrics allMetrics =
+                dpms.getPasswordMinimumMetrics(UserHandle.USER_SYSTEM, false);
+        assertThat(allMetrics.credType).isEqualTo(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
+        assertThat(allMetrics.length).isEqualTo(8);
+        assertThat(allMetrics.letters).isEqualTo(1);
+        assertThat(allMetrics.numeric).isEqualTo(2);
+        assertThat(allMetrics.symbols).isEqualTo(3);
+    }
+
+    @Test
     public void testCanSetPasswordRequirementOnParentPreS() throws Exception {
         final int managedProfileUserId = CALLER_USER_HANDLE;
         final int managedProfileAdminUid =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 6e4d994..44418ce 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -105,6 +105,10 @@
                     }
 
                     @Override
+                    protected void writeStringSystemProperty(String key, String value) {
+                    }
+
+                    @Override
                     Looper getServiceLooper() {
                         return mTestLooper.getLooper();
                     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index bbe1156..d454d87 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -106,6 +106,10 @@
                     }
 
                     @Override
+                    protected void writeStringSystemProperty(String key, String value) {
+                    }
+
+                    @Override
                     Looper getServiceLooper() {
                         return mTestLooper.getLooper();
                     }
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 aeeca1a..8e37f94 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -95,6 +95,15 @@
                     + "    </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>"
                     + "</cec-settings>";
 
     FakeHdmiCecConfig(@NonNull Context context) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 6bb68da..95a0a74 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -195,8 +195,9 @@
                 }
             };
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
-
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mMyLooper = mTestLooper.getLooper();
         mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
         mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
@@ -710,7 +711,8 @@
     public void giveAudioStatus_volumeEnabled() {
         mMusicVolume = 50;
         mMusicMaxVolume = 100;
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
 
         int volume = mHdmiControlService.getAudioManager()
@@ -740,7 +742,8 @@
     public void giveAudioStatus_volumeDisabled() {
         mMusicVolume = 50;
         mMusicMaxVolume = 100;
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
 
         int volume = mHdmiControlService.getAudioManager()
@@ -770,7 +773,8 @@
     public void reportAudioStatus_volumeEnabled() {
         mMusicVolume = 50;
         mMusicMaxVolume = 100;
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
 
         int volume = mHdmiControlService.getAudioManager()
@@ -794,7 +798,8 @@
     public void reportAudioStatus_volumeDisabled() {
         mMusicVolume = 50;
         mMusicMaxVolume = 100;
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
 
         int volume = mHdmiControlService.getAudioManager()
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 7aea4ff..bc80876 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -862,7 +862,8 @@
 
     @Test
     public void sendVolumeKeyEvent_up_volumeEnabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
         mTestLooper.dispatchAll();
@@ -879,7 +880,8 @@
 
     @Test
     public void sendVolumeKeyEvent_down_volumeEnabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
         mTestLooper.dispatchAll();
@@ -896,7 +898,8 @@
 
     @Test
     public void sendVolumeKeyEvent_mute_volumeEnabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
         mTestLooper.dispatchAll();
@@ -913,7 +916,8 @@
 
     @Test
     public void sendVolumeKeyEvent_up_volumeDisabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
         mTestLooper.dispatchAll();
@@ -930,7 +934,8 @@
 
     @Test
     public void sendVolumeKeyEvent_down_volumeDisabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
         mTestLooper.dispatchAll();
@@ -947,7 +952,8 @@
 
     @Test
     public void sendVolumeKeyEvent_mute_volumeDisabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true);
         mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
         mTestLooper.dispatchAll();
@@ -964,7 +970,8 @@
 
     @Test
     public void sendVolumeKeyEvent_toTv_activeSource() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiControlService.setSystemAudioActivated(false);
         mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress,
                 "HdmiCecLocalDevicePlaybackTest");
@@ -984,7 +991,8 @@
 
     @Test
     public void sendVolumeKeyEvent_toAudio_activeSource() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiControlService.setSystemAudioActivated(true);
         mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress,
                 "HdmiCecLocalDevicePlaybackTest");
@@ -1004,7 +1012,8 @@
 
     @Test
     public void sendVolumeKeyEvent_toTv_inactiveSource() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiControlService.setSystemAudioActivated(false);
         mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest");
 
@@ -1023,7 +1032,8 @@
 
     @Test
     public void sendVolumeKeyEvent_toAudio_inactiveSource() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mHdmiControlService.setSystemAudioActivated(true);
         mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest");
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index eb2f960..0717112 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -32,6 +32,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.content.Context;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.os.test.TestLooper;
@@ -124,8 +125,13 @@
 
     @Before
     public void SetUp() {
+
+        Context context = InstrumentationRegistry.getTargetContext();
+
+        HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
+
         mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                new HdmiControlService(context) {
                     @Override
                     boolean isControlEnabled() {
                         return isControlEnabled;
@@ -157,6 +163,11 @@
                     void wakeUp() {
                         mWakeupMessageReceived = true;
                     }
+
+                    @Override
+                    protected HdmiCecConfig getHdmiCecConfig() {
+                        return hdmiCecConfig;
+                    }
                 };
         mHdmiControlService.setIoLooper(mTestLooper.getLooper());
         mNativeWrapper = new FakeNativeWrapper();
@@ -244,7 +255,8 @@
 
     @Test
     public void handleUserControlPressed_volumeUp() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         boolean result = mHdmiLocalDevice.handleUserControlPressed(
                 HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
                         HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP));
@@ -254,7 +266,8 @@
 
     @Test
     public void handleUserControlPressed_volumeDown() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         boolean result = mHdmiLocalDevice.handleUserControlPressed(
                 HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
                         HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
@@ -264,7 +277,8 @@
 
     @Test
     public void handleUserControlPressed_volumeMute() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         boolean result = mHdmiLocalDevice.handleUserControlPressed(
                 HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
                         HdmiCecKeycode.CEC_KEYCODE_MUTE));
@@ -274,7 +288,8 @@
 
     @Test
     public void handleUserControlPressed_volumeUp_disabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         boolean result = mHdmiLocalDevice.handleUserControlPressed(
                 HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
                         HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP));
@@ -284,7 +299,8 @@
 
     @Test
     public void handleUserControlPressed_volumeDown_disabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         boolean result = mHdmiLocalDevice.handleUserControlPressed(
                 HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
                         HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
@@ -294,7 +310,8 @@
 
     @Test
     public void handleUserControlPressed_volumeMute_disabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         boolean result = mHdmiLocalDevice.handleUserControlPressed(
                 HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
                         HdmiCecKeycode.CEC_KEYCODE_MUTE));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
index c212bf8..70718f7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -136,6 +136,8 @@
         // Some tests expect no logical addresses being allocated at the beginning of the test.
         setHdmiControlEnabled(false);
 
+        HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContext);
+
         mHdmiControlService =
             new HdmiControlService(mContext) {
                 @Override
@@ -158,6 +160,11 @@
                 boolean isPowerStandby() {
                     return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
                 }
+
+                @Override
+                protected HdmiCecConfig getHdmiCecConfig() {
+                    return hdmiCecConfig;
+                }
             };
         mMyLooper = mTestLooper.getLooper();
 
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 9152e1e..be584d7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -154,6 +154,10 @@
             }
 
             @Override
+            protected void writeStringSystemProperty(String key, String value) {
+            }
+
+            @Override
             protected HdmiCecConfig getHdmiCecConfig() {
                 return hdmiCecConfig;
             }
@@ -251,7 +255,7 @@
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
 
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         mNativeWrapper.clearResultMessages();
 
         assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
@@ -271,7 +275,7 @@
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
 
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         mNativeWrapper.clearResultMessages();
 
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
@@ -291,136 +295,175 @@
 
     @Test
     public void setAndGetCecVolumeControlEnabled_isApi() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
-        assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
+        assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
-        assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue();
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
+        assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
     public void setAndGetCecVolumeControlEnabled_changesSetting() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
-        assertThat(mHdmiControlService.readBooleanSetting(
-                Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isFalse();
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
+        assertThat(mHdmiControlService.readIntSetting(
+                Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, -1)).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
-        assertThat(mHdmiControlService.readBooleanSetting(
-                Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isTrue();
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
+        assertThat(mHdmiControlService.readIntSetting(
+                Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, -1)).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
     public void setAndGetCecVolumeControlEnabledInternal_doesNotChangeSetting() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(true);
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(false);
-        assertThat(mHdmiControlService.readBooleanSetting(
-                Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isTrue();
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
+        assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(true);
-        assertThat(mHdmiControlService.readBooleanSetting(
-                Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)).isTrue();
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
+        assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
     public void disableAndReenableCec_volumeControlReturnsToOriginalValue_enabled() {
-        boolean volumeControlEnabled = true;
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled);
+        int volumeControlEnabled = HdmiControlManager.VOLUME_CONTROL_ENABLED;
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(volumeControlEnabled);
 
-        mHdmiControlService.setControlEnabled(false);
-        assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+        assertThat(mHdmiControlService.getHdmiCecVolumeControl()).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
 
-        mHdmiControlService.setControlEnabled(true);
-        assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isEqualTo(
-                volumeControlEnabled);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+        assertThat(mHdmiControlService.getHdmiCecVolumeControl()).isEqualTo(volumeControlEnabled);
     }
 
     @Test
     public void disableAndReenableCec_volumeControlReturnsToOriginalValue_disabled() {
-        boolean volumeControlEnabled = false;
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled);
+        int volumeControlEnabled = HdmiControlManager.VOLUME_CONTROL_DISABLED;
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, volumeControlEnabled);
 
-        mHdmiControlService.setControlEnabled(false);
-        assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+        assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo(
+                volumeControlEnabled);
 
-        mHdmiControlService.setControlEnabled(true);
-        assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isEqualTo(
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+        assertThat(mHdmiControlService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE)).isEqualTo(
                 volumeControlEnabled);
     }
 
     @Test
     public void disableAndReenableCec_volumeControlFeatureListenersNotified() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
 
         VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
         mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
 
-        mHdmiControlService.setControlEnabled(false);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
         assertThat(callback.mCallbackReceived).isTrue();
-        assertThat(callback.mVolumeControlEnabled).isFalse();
+        assertThat(callback.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
 
 
-        mHdmiControlService.setControlEnabled(true);
-        assertThat(callback.mVolumeControlEnabled).isTrue();
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+        assertThat(callback.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
     public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_enabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
 
         mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
         mTestLooper.dispatchAll();
 
         assertThat(callback.mCallbackReceived).isTrue();
-        assertThat(callback.mVolumeControlEnabled).isTrue();
+        assertThat(callback.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
     public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_disabled() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
 
         mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
         mTestLooper.dispatchAll();
 
         assertThat(callback.mCallbackReceived).isTrue();
-        assertThat(callback.mVolumeControlEnabled).isFalse();
+        assertThat(callback.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
     }
 
     @Test
     public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
 
         mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mTestLooper.dispatchAll();
 
         assertThat(callback.mCallbackReceived).isTrue();
-        assertThat(callback.mVolumeControlEnabled).isTrue();
+        assertThat(callback.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
     public void addHdmiCecVolumeControlFeatureListener_honorsUnregistration() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
 
         mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
         mTestLooper.dispatchAll();
 
         mHdmiControlService.removeHdmiControlVolumeControlStatusChangeListener(callback);
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mTestLooper.dispatchAll();
 
         assertThat(callback.mCallbackReceived).isTrue();
-        assertThat(callback.mVolumeControlEnabled).isFalse();
+        assertThat(callback.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
     }
 
     @Test
     public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate_multiple() {
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_DISABLED);
         VolumeControlFeatureCallback callback1 = new VolumeControlFeatureCallback();
         VolumeControlFeatureCallback callback2 = new VolumeControlFeatureCallback();
 
@@ -428,13 +471,16 @@
         mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback2);
 
 
-        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setHdmiCecVolumeControlEnabledInternal(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
         mTestLooper.dispatchAll();
 
         assertThat(callback1.mCallbackReceived).isTrue();
         assertThat(callback2.mCallbackReceived).isTrue();
-        assertThat(callback1.mVolumeControlEnabled).isTrue();
-        assertThat(callback2.mVolumeControlEnabled).isTrue();
+        assertThat(callback1.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
+        assertThat(callback2.mVolumeControlEnabled).isEqualTo(
+                HdmiControlManager.VOLUME_CONTROL_ENABLED);
     }
 
     @Test
@@ -443,7 +489,7 @@
         Settings.Global.putString(mContextSpy.getContentResolver(),
                 Settings.Global.HDMI_CEC_VERSION,
                 null);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
     }
@@ -453,7 +499,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
     }
@@ -463,7 +509,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
     }
@@ -473,14 +519,14 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
 
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
     }
@@ -490,7 +536,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         mTestLooper.dispatchAll();
 
         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveFeatures(Constants.ADDR_TV,
@@ -508,7 +554,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
@@ -530,7 +576,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
@@ -547,7 +593,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
-        mHdmiControlService.setControlEnabled(true);
+        mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
@@ -579,10 +625,10 @@
     private static class VolumeControlFeatureCallback extends
             IHdmiCecVolumeControlFeatureListener.Stub {
         boolean mCallbackReceived = false;
-        boolean mVolumeControlEnabled = false;
+        int mVolumeControlEnabled = -1;
 
         @Override
-        public void onHdmiCecVolumeControlFeature(boolean enabled) throws RemoteException {
+        public void onHdmiCecVolumeControlFeature(int enabled) throws RemoteException {
             this.mCallbackReceived = true;
             this.mVolumeControlEnabled = enabled;
         }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index f80b573..f9160ab 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -138,6 +138,10 @@
                     }
 
                     @Override
+                    protected void writeStringSystemProperty(String key, String value) {
+                    }
+
+                    @Override
                     void wakeUp() {}
 
                     @Override
diff --git a/services/tests/servicestests/src/com/android/server/textservices/OWNERS b/services/tests/servicestests/src/com/android/server/textservices/OWNERS
new file mode 100644
index 0000000..0471e29
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/textservices/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 816455
+
+include /services/core/java/com/android/server/textservices/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
new file mode 100644
index 0000000..8ac6dfb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.timedetector;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.app.timedetector.GnssTimeSuggestion;
+import android.app.timedetector.TimeDetector;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.LocationRequest;
+import android.location.LocationTime;
+import android.os.TimestampedValue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public final class GnssTimeUpdateServiceTest {
+    private static final long GNSS_TIME = 999_999_999L;
+    private static final long ELAPSED_REALTIME_NS = 123_000_000L;
+    private static final long ELAPSED_REALTIME_MS = ELAPSED_REALTIME_NS / 1_000_000L;
+
+    @Mock private Context mMockContext;
+    @Mock private TimeDetector mMockTimeDetector;
+    @Mock private AlarmManager mMockAlarmManager;
+    @Mock private LocationManager mMockLocationManager;
+    @Mock private LocationManagerInternal mLocationManagerInternal;
+
+    private GnssTimeUpdateService mGnssTimeUpdateService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockContext.createAttributionContext(anyString()))
+                .thenReturn(mMockContext);
+
+        when(mMockContext.getSystemServiceName(TimeDetector.class))
+                .thenReturn((TimeDetector.class).getSimpleName());
+        when(mMockContext.getSystemService(TimeDetector.class))
+                .thenReturn(mMockTimeDetector);
+
+        when(mMockContext.getSystemServiceName(LocationManager.class))
+                .thenReturn((LocationManager.class).getSimpleName());
+        when(mMockContext.getSystemService(LocationManager.class))
+                .thenReturn(mMockLocationManager);
+
+        when(mMockContext.getSystemServiceName(AlarmManager.class))
+                .thenReturn((AlarmManager.class).getSimpleName());
+        when(mMockContext.getSystemService(AlarmManager.class))
+                .thenReturn(mMockAlarmManager);
+
+        LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
+
+        mGnssTimeUpdateService =
+                new GnssTimeUpdateService(mMockContext);
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(LocationManagerInternal.class);
+    }
+
+    @Test
+    public void testLocationListenerOnLocationChanged_validLocationTime_suggestsGnssTime() {
+        TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+                ELAPSED_REALTIME_MS, GNSS_TIME);
+        GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal);
+        LocationTime locationTime = new LocationTime(GNSS_TIME, ELAPSED_REALTIME_NS);
+        doReturn(locationTime).when(mLocationManagerInternal).getGnssTimeMillis();
+
+        mGnssTimeUpdateService.requestGnssTimeUpdates();
+
+        ArgumentCaptor<LocationListener> argumentCaptor =
+                ArgumentCaptor.forClass(LocationListener.class);
+        verify(mMockLocationManager).requestLocationUpdates(
+                eq(LocationManager.GPS_PROVIDER),
+                eq(new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL)
+                    .setMinUpdateIntervalMillis(0)
+                    .build()),
+                any(),
+                argumentCaptor.capture());
+        LocationListener locationListener = argumentCaptor.getValue();
+        Location location = new Location(LocationManager.GPS_PROVIDER);
+
+        locationListener.onLocationChanged(location);
+
+        verify(mMockLocationManager).removeUpdates(locationListener);
+        verify(mMockTimeDetector).suggestGnssTime(timeSuggestion);
+        verify(mMockAlarmManager).set(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                anyLong(),
+                any(),
+                any(),
+                any());
+    }
+
+    @Test
+    public void testLocationListenerOnLocationChanged_nullLocationTime_doesNotSuggestGnssTime() {
+        doReturn(null).when(mLocationManagerInternal).getGnssTimeMillis();
+
+        mGnssTimeUpdateService.requestGnssTimeUpdates();
+
+        ArgumentCaptor<LocationListener> argumentCaptor =
+                ArgumentCaptor.forClass(LocationListener.class);
+        verify(mMockLocationManager).requestLocationUpdates(
+                eq(LocationManager.GPS_PROVIDER),
+                eq(new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL)
+                    .setMinUpdateIntervalMillis(0)
+                    .build()),
+                any(),
+                argumentCaptor.capture());
+        LocationListener locationListener = argumentCaptor.getValue();
+        Location location = new Location(LocationManager.GPS_PROVIDER);
+
+        locationListener.onLocationChanged(location);
+
+        verify(mMockLocationManager).removeUpdates(locationListener);
+        verify(mMockTimeDetector, never()).suggestGnssTime(any());
+        verify(mMockAlarmManager).set(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                anyLong(),
+                any(),
+                any(),
+                any());
+    }
+}
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 6f584ee..ef96a2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1064,6 +1064,21 @@
     }
 
     /**
+     * Verify that finish bottom activity from a task won't boost it to top.
+     */
+    @Test
+    public void testFinishBottomActivityIfPossible_noZBoost() {
+        final ActivityRecord bottomActivity = createActivityWithTask();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+                .setTask(bottomActivity.getTask()).build();
+        topActivity.mVisibleRequested = true;
+        // simulating bottomActivity as a trampoline activity.
+        bottomActivity.setState(RESUMED, "test");
+        bottomActivity.finishIfPossible("test", false);
+        assertFalse(bottomActivity.mNeedsZBoost);
+    }
+
+    /**
      * Verify that complete finish request for visible activity must be delayed before the next one
      * becomes visible.
      */
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 717040a..089a948 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3014,6 +3014,26 @@
     public void onCallAudioStateChanged(CallAudioState state) {}
 
     /**
+     * Inform this Connection when it will or will not be tracked by an {@link InCallService} which
+     * can provide an InCall UI.
+     * This is primarily intended for use by Connections reported by self-managed
+     * {@link ConnectionService} which typically maintain their own UI.
+     *
+     * @param isUsingAlternativeUi Indicates whether an InCallService that can provide InCall UI is
+     *                             currently tracking the self-managed call.
+     */
+    public void onUsingAlternativeUi(boolean isUsingAlternativeUi) {}
+
+    /**
+     * Inform this Conenection when it will or will not be tracked by an non-UI
+     * {@link InCallService}.
+     *
+     * @param isTracked Indicates whether an non-UI InCallService is currently tracking the
+     *                 self-managed call.
+     */
+    public void onTrackedByNonUiService(boolean isTracked) {}
+
+    /**
      * Notifies this Connection of an internal state change. This method is called after the
      * state is changed.
      *
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index f86f9d5..5c75a2f 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -137,6 +137,8 @@
     private static final String SESSION_HOLD = "CS.h";
     private static final String SESSION_UNHOLD = "CS.u";
     private static final String SESSION_CALL_AUDIO_SC = "CS.cASC";
+    private static final String SESSION_USING_ALTERNATIVE_UI = "CS.uAU";
+    private static final String SESSION_TRACKED_BY_NON_UI_SERVICE = "CS.tBNUS";
     private static final String SESSION_PLAY_DTMF = "CS.pDT";
     private static final String SESSION_STOP_DTMF = "CS.sDT";
     private static final String SESSION_CONFERENCE = "CS.c";
@@ -202,6 +204,8 @@
     private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
     private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
     private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
+    private static final int MSG_ON_USING_ALTERNATIVE_UI = 43;
+    private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44;
 
     private static Connection sNullConnection;
 
@@ -586,6 +590,36 @@
         }
 
         @Override
+        public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = usingAlternativeUiShowing;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ON_USING_ALTERNATIVE_UI, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void onTrackedByNonUiService(String callId, boolean isTracked,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_TRACKED_BY_NON_UI_SERVICE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = isTracked;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ON_TRACKED_BY_NON_UI_SERVICE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_PLAY_DTMF);
             try {
@@ -1244,6 +1278,34 @@
                     }
                     break;
                 }
+                case MSG_ON_USING_ALTERNATIVE_UI: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3,
+                            SESSION_HANDLER + SESSION_USING_ALTERNATIVE_UI);
+                    try {
+                        String callId = (String) args.arg1;
+                        boolean isUsingAlternativeUi = (boolean) args.arg2;
+                        onUsingAlternativeUi(callId, isUsingAlternativeUi);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+                case MSG_ON_TRACKED_BY_NON_UI_SERVICE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3,
+                            SESSION_HANDLER + SESSION_TRACKED_BY_NON_UI_SERVICE);
+                    try {
+                        String callId = (String) args.arg1;
+                        boolean isTracked = (boolean) args.arg2;
+                        onTrackedByNonUiService(callId, isTracked);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_PLAY_DTMF_TONE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -1961,10 +2023,12 @@
                 request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
         boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
                 TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
-        Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
-                        "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
-                callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
-                isHandover);
+        boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean(
+                PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false);
+        Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, "
+                        + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, "
+                        + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming,
+                isUnknown, isLegacyHandover, isHandover, addSelfManaged);
 
         Connection connection = null;
         if (isHandover) {
@@ -2239,6 +2303,22 @@
         }
     }
 
+    private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) {
+        Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "onUsingAlternativeUi")
+                    .onUsingAlternativeUi(isUsingAlternativeUi);
+        }
+    }
+
+    private void onTrackedByNonUiService(String callId, boolean isTracked) {
+        Log.i(this, "onTrackedByNonUiService %s %s", callId, isTracked);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "onTrackedByNonUiService")
+                    .onTrackedByNonUiService(isTracked);
+        }
+    }
+
     private void playDtmfTone(String callId, char digit) {
         Log.i(this, "playDtmfTone %s %c", callId, digit);
         if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 835ecaa..579b33e 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -188,6 +188,15 @@
         "android.telecom.extra.SKIP_CALL_FILTERING";
 
     /**
+     * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+     * indicates whether a Self-managed {@link PhoneAccount} want to expose its calls to all
+     * {@link InCallService} which declares the metadata
+     * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS}.
+     */
+    public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE =
+            "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE";
+
+    /**
      * Flag indicating that this {@code PhoneAccount} can act as a connection manager for
      * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
      * will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 92264be..301c2bb 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -139,4 +139,9 @@
             int error, in Session.Info sessionInfo);
 
     void handoverComplete(String callId, in Session.Info sessionInfo);
+
+    void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi,
+            in Session.Info sessionInfo);
+
+    void onTrackedByNonUiService(String callId, boolean isTracked, in Session.Info sessionInfo);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index daa3d1f0..8041fa9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4638,6 +4638,18 @@
             "use_lower_mtu_value_if_both_received";
 
     /**
+     * Determines the default RTT mode.
+     *
+     * Upon first boot, when the user has not yet set a value for their preferred RTT mode,
+     * the value of this config will be sent to the IMS stack. Valid values are the same as for
+     * {@link Settings.Secure#RTT_CALLING_MODE}.
+     *
+     * @hide
+     */
+    public static final String KEY_DEFAULT_RTT_MODE_INT =
+            "default_rtt_mode_int";
+
+    /**
      * Indicates if auto-configuration server is used for the RCS config
      * Reference: GSMA RCC.14
      */
@@ -5201,6 +5213,7 @@
         sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
         sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
         sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
+        sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index e06dcdb..6c013df 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -384,7 +384,6 @@
      * where this operation may fail.
      * </p>
      *
-     *
      * @param destinationAddress the address to send the message to
      * @param scAddress is the service center address or null to use
      *  the current default SMSC
@@ -397,7 +396,6 @@
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
      *  <code>RESULT_ERROR_NULL_PDU</code><br>
      *  <code>RESULT_ERROR_NO_SERVICE</code><br>
-     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
      *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
      *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
      *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
@@ -514,7 +512,6 @@
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
      *  <code>RESULT_ERROR_NULL_PDU</code><br>
      *  <code>RESULT_ERROR_NO_SERVICE</code><br>
-     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
      *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
      *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
      *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
@@ -903,22 +900,20 @@
      * where this operation may fail.
      * </p>
      *
-     *
      * @param destinationAddress the address to send the message to
      * @param scAddress is the service center address or null to use
-     *   the current default SMSC
+     *  the current default SMSC
      * @param parts an <code>ArrayList</code> of strings that, in order,
-     *   comprise the original message
+     *  comprise the original message
      * @param sentIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been sent.
-     *   The result code will be <code>Activity.RESULT_OK</code> for success,
-     *   or one of these errors:<br>
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been sent.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
      *  <code>RESULT_ERROR_NULL_PDU</code><br>
      *  <code>RESULT_ERROR_NO_SERVICE</code><br>
-     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
      *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
      *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
      *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
@@ -974,14 +969,14 @@
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
-     *   The per-application based SMS control checks sentIntent. If sentIntent
-     *   is NULL the caller will be checked against all unknown applications,
-     *   which cause smaller number of SMS to be sent in checking period.
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
      * @param deliveryIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been delivered
-     *   to the recipient.  The raw pdu of the status report is in the
-     *   extended data ("pdu").
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been delivered
+     *  to the recipient.  The raw pdu of the status report is in the
+     *  extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
      */
@@ -1163,22 +1158,21 @@
      * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
      * where this operation may fail.
      * </p>
-
+     *
      * @param destinationAddress the address to send the message to
      * @param scAddress is the service center address or null to use
-     *   the current default SMSC
+     *  the current default SMSC
      * @param parts an <code>ArrayList</code> of strings that, in order,
-     *   comprise the original message
+     *  comprise the original message
      * @param sentIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been sent.
-     *   The result code will be <code>Activity.RESULT_OK</code> for success,
-     *   or one of these errors:<br>
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been sent.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
      *  <code>RESULT_ERROR_NULL_PDU</code><br>
      *  <code>RESULT_ERROR_NO_SERVICE</code><br>
-     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
      *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
      *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
      *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
@@ -1234,14 +1228,14 @@
      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
      *  the sentIntent may include the extra "errorCode" containing a radio technology specific
      *  value, generally only useful for troubleshooting.<br>
-     *   The per-application based SMS control checks sentIntent. If sentIntent
-     *   is NULL the caller will be checked against all unknown applications,
-     *   which cause smaller number of SMS to be sent in checking period.
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
      * @param deliveryIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been delivered
-     *   to the recipient.  The raw pdu of the status report is in the
-     *   extended data ("pdu").
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been delivered
+     *  to the recipient.  The raw pdu of the status report is in the
+     *  extended data ("pdu").
      * @param priority Priority level of the message
      *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
      *  ---------------------------------
@@ -1380,7 +1374,6 @@
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
      *  <code>RESULT_ERROR_NULL_PDU</code><br>
      *  <code>RESULT_ERROR_NO_SERVICE</code><br>
-     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
      *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
      *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
      *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 2ebb9c1..94950dc 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -16,11 +16,11 @@
 
 package com.android.tests.rollback.host;
 
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.tests.rollback.host.WatchdogEventLogger.Subject.assertThat;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
@@ -62,9 +62,9 @@
      * For example, <code>runPhase("testApkOnlyEnableRollback");</code>
      */
     private void runPhase(String phase) throws Exception {
-        assertTrue(runDeviceTests("com.android.tests.rollback",
+        assertThat(runDeviceTests("com.android.tests.rollback",
                     "com.android.tests.rollback.StagedRollbackTest",
-                    phase));
+                    phase)).isTrue();
     }
 
     private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
@@ -150,17 +150,15 @@
 
         // Trigger rollback and wait for reboot to happen
         runPhase("testBadApkOnly_Phase3");
-        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2)));
+        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2))).isTrue();
 
         getDevice().waitForDeviceAvailable();
 
         runPhase("testBadApkOnly_Phase4");
 
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
-                REASON_APP_CRASH, TESTAPP_A));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
-                null, null));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
+        assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
+        assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
+        assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
     }
 
     @Test
@@ -183,17 +181,15 @@
         // 3. Staged rollback session becomes ready.
         // 4. Device actually reboots.
         // So we give a generous timeout here.
-        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue();
         getDevice().waitForDeviceAvailable();
 
         // verify rollback committed
         runPhase("testNativeWatchdogTriggersRollback_Phase3");
 
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
-                        REASON_NATIVE_CRASH, null));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
-                null, null));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
+        assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null);
+        assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
+        assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
     }
 
     @Test
@@ -223,17 +219,15 @@
         // 3. Staged rollback session becomes ready.
         // 4. Device actually reboots.
         // So we give a generous timeout here.
-        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue();
         getDevice().waitForDeviceAvailable();
 
         // verify all available rollbacks have been committed
         runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4");
 
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
-                        REASON_NATIVE_CRASH, null));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
-                null, null));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
+        assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null);
+        assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
+        assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
     }
 
     /**
@@ -306,16 +300,14 @@
         // Verify apex was installed and then crash the apk
         runPhase("testRollbackApexWithApkCrashing_Phase2");
         // Wait for crash to trigger rollback
-        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+        assertThat(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))).isTrue();
         getDevice().waitForDeviceAvailable();
         // Verify rollback occurred due to crash of apk-in-apex
         runPhase("testRollbackApexWithApkCrashing_Phase3");
 
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
-                REASON_APP_CRASH, TESTAPP_A));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
-                null, null));
-        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
+        assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
+        assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
+        assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
     }
 
     /**
@@ -356,10 +348,10 @@
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-            assertNull(getDevice().pullFile(newFilePath3));
-            assertNull(getDevice().pullFile(newFilePath4));
+            assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1);
+            assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2);
+            assertThat(getDevice().pullFile(newFilePath3)).isNull();
+            assertThat(getDevice().pullFile(newFilePath4)).isNull();
         });
 
         // Verify snapshots are deleted after restoration
@@ -411,10 +403,10 @@
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-            assertNull(getDevice().pullFile(newFilePath3));
-            assertNull(getDevice().pullFile(newFilePath4));
+            assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1);
+            assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2);
+            assertThat(getDevice().pullFile(newFilePath3)).isNull();
+            assertThat(getDevice().pullFile(newFilePath4)).isNull();
         });
 
         // Verify snapshots are deleted after restoration
@@ -464,10 +456,10 @@
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-            assertNull(getDevice().pullFile(newFilePath3));
-            assertNull(getDevice().pullFile(newFilePath4));
+            assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1);
+            assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2);
+            assertThat(getDevice().pullFile(newFilePath3)).isNull();
+            assertThat(getDevice().pullFile(newFilePath4)).isNull();
         });
 
         // Verify snapshots are deleted after restoration
@@ -515,10 +507,10 @@
 
         // Verify that old files have been restored and new files are gone
         runAsRoot(() -> {
-            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-            assertNull(getDevice().pullFile(newFilePath3));
-            assertNull(getDevice().pullFile(newFilePath4));
+            assertThat(getDevice().pullFileContents(oldFilePath1)).isEqualTo(TEST_STRING_1);
+            assertThat(getDevice().pullFileContents(oldFilePath2)).isEqualTo(TEST_STRING_2);
+            assertThat(getDevice().pullFile(newFilePath3)).isNull();
+            assertThat(getDevice().pullFile(newFilePath4)).isNull();
         });
     }
 
@@ -549,7 +541,7 @@
         runPhase("testCleanUp");
         runAsRoot(() -> {
             for (String dir : after) {
-                assertNull(getDevice().getFileEntry(dir));
+                assertThat(getDevice().getFileEntry(dir)).isNull();
             }
         });
     }
@@ -561,7 +553,7 @@
         try {
             getDevice().enableAdbRoot();
             getDevice().remountSystemWritable();
-            assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+            assertThat(getDevice().pushFile(apex, "/system/apex/" + fileName)).isTrue();
         } finally {
             getDevice().disableAdbRoot();
         }
@@ -607,8 +599,9 @@
         try {
             getDevice().enableAdbRoot();
             IFileEntry file = getDevice().getFileEntry(path);
-            assertTrue("Not a directory: " + path, file.isDirectory());
-            assertTrue("Directory not empty: " + path, file.getChildren(false).isEmpty());
+            assertWithMessage("Not a directory: " + path).that(file.isDirectory()).isTrue();
+            assertWithMessage("Directory not empty: " + path)
+                    .that(file.getChildren(false)).isEmpty();
         } catch (DeviceNotAvailableException e) {
             fail("Can't access directory: " + path);
         } finally {
diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
index 6b0d1f8..8c16079 100644
--- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
+++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
@@ -17,6 +17,8 @@
 package com.android.tests.rollback.host;
 
 import com.android.tradefed.device.ITestDevice;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Truth;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -76,4 +78,30 @@
                 && matchProperty(type, "rollbackReason", rollbackReason)
                 && matchProperty(type, "failedPackageName", failedPackageName);
     }
+
+    static class Subject extends com.google.common.truth.Subject {
+        private final WatchdogEventLogger mActual;
+
+        private Subject(FailureMetadata failureMetadata, WatchdogEventLogger subject) {
+            super(failureMetadata, subject);
+            mActual = subject;
+        }
+
+        private static com.google.common.truth.Subject.Factory<Subject,
+                WatchdogEventLogger> loggers() {
+            return Subject::new;
+        }
+
+        static Subject assertThat(WatchdogEventLogger actual) {
+            return Truth.assertAbout(loggers()).that(actual);
+        }
+
+        void eventOccurred(String type, String logPackage, String rollbackReason,
+                String failedPackageName) throws Exception {
+            check("watchdogEventOccurred(type=%s, logPackage=%s, rollbackReason=%s, "
+                    + "failedPackageName=%s)", type, logPackage, rollbackReason, failedPackageName)
+                    .that(mActual.watchdogEventOccurred(type, logPackage, rollbackReason,
+                            failedPackageName)).isTrue();
+        }
+    }
 }